Browse Source

Merge pull request #4624 from AvaloniaUI/angle-display

Angle display
pull/4644/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
fb9fa94037
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 31
      src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
  2. 88
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  3. 10
      src/Avalonia.OpenGL/EglConsts.cs
  4. 80
      src/Avalonia.OpenGL/EglDisplay.cs
  5. 7
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  6. 84
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  7. 103
      src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs
  8. 29
      src/Avalonia.OpenGL/EglInterface.cs
  9. 3
      src/Windows/Avalonia.Win32/Win32GlManager.cs

31
src/Avalonia.OpenGL/Angle/AngleEglInterface.cs

@ -0,0 +1,31 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Platform;
using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL.Angle
{
public class AngleEglInterface : EglInterface
{
[DllImport("libegl.dll", CharSet = CharSet.Ansi)]
static extern IntPtr eglGetProcAddress(string proc);
public AngleEglInterface() : base(LoadAngle())
{
}
static Func<string, IntPtr> LoadAngle()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
throw new PlatformNotSupportedException();
{
var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
if (disp == IntPtr.Zero)
throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
return eglGetProcAddress;
}
}
}
}

88
src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL.Angle
{
public class AngleWin32EglDisplay : EglDisplay
{
struct AngleInfo
{
public IntPtr Display { get; set; }
public AngleOptions.PlatformApi PlatformApi { get; set; }
}
static AngleInfo CreateAngleDisplay(EglInterface _egl)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
throw new PlatformNotSupportedException();
var display = IntPtr.Zero;
AngleOptions.PlatformApi angleApi = default;
{
if (_egl.GetPlatformDisplayEXT == null)
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
var allowedApis = AvaloniaLocator.Current.GetService<AngleOptions>()?.AllowedPlatformApis
?? new [] { AngleOptions.PlatformApi.DirectX11, AngleOptions.PlatformApi.DirectX9 };
foreach (var platformApi in allowedApis)
{
int dapi;
if (platformApi == AngleOptions.PlatformApi.DirectX9)
dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
else if (platformApi == AngleOptions.PlatformApi.DirectX11)
dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
else
continue;
display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
new[] { EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE });
if (display != IntPtr.Zero)
{
angleApi = platformApi;
break;
}
}
if (display == IntPtr.Zero)
throw new OpenGlException("Unable to create ANGLE display");
return new AngleInfo { Display = display, PlatformApi = angleApi };
}
}
private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, info.Display)
{
PlatformApi = info.PlatformApi;
}
public AngleWin32EglDisplay(EglInterface egl) : this(egl, CreateAngleDisplay(egl))
{
}
public AngleWin32EglDisplay() : this(new AngleEglInterface())
{
}
public AngleOptions.PlatformApi PlatformApi { get; }
public IntPtr GetDirect3DDevice()
{
if (!EglInterface.QueryDisplayAttribExt(Handle, EglConsts.EGL_DEVICE_EXT, out var eglDevice))
throw new OpenGlException("Unable to get EGL_DEVICE_EXT");
if (!EglInterface.QueryDeviceAttribExt(eglDevice, PlatformApi == AngleOptions.PlatformApi.DirectX9 ? EGL_D3D9_DEVICE_ANGLE : EGL_D3D11_DEVICE_ANGLE, out var d3dDeviceHandle))
throw new OpenGlException("Unable to get EGL_D3D9_DEVICE_ANGLE");
return d3dDeviceHandle;
}
public EglSurface WrapDirect3D11Texture(IntPtr handle)
{
if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi);
return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
}
}
}

10
src/Avalonia.OpenGL/EglConsts.cs

@ -195,5 +195,15 @@ namespace Avalonia.OpenGL
public const int EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE = 0x320F; 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_WARP_ANGLE = 0x320B;
public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE = 0x320C; public const int EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE = 0x320C;
//EXT_device_query
public const int EGL_DEVICE_EXT = 0x322C;
//ANGLE_device_d3d
public const int EGL_D3D9_DEVICE_ANGLE = 0x33A0;
public const int EGL_D3D11_DEVICE_ANGLE = 0x33A1;
public const int EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE = 0x3200;
public const int EGL_D3D_TEXTURE_ANGLE = 0x33A3;
} }
} }

80
src/Avalonia.OpenGL/EglDisplay.cs

@ -15,7 +15,6 @@ namespace Avalonia.OpenGL
private readonly int _surfaceType; private readonly int _surfaceType;
public IntPtr Handle => _display; public IntPtr Handle => _display;
private AngleOptions.PlatformApi? _angleApi;
private int _sampleCount; private int _sampleCount;
private int _stencilSize; private int _stencilSize;
private GlVersion _version; private GlVersion _version;
@ -24,60 +23,41 @@ namespace Avalonia.OpenGL
{ {
} }
public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
{
_egl = egl;
static IntPtr CreateDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
{
var display = IntPtr.Zero;
if (platformType == -1 && platformDisplay == IntPtr.Zero) if (platformType == -1 && platformDisplay == IntPtr.Zero)
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (display == IntPtr.Zero)
{ display = egl.GetDisplay(IntPtr.Zero);
if (_egl.GetPlatformDisplayEXT == null)
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl.dll");
var allowedApis = AvaloniaLocator.Current.GetService<AngleOptions>()?.AllowedPlatformApis
?? new []
{
AngleOptions.PlatformApi.DirectX11,
AngleOptions.PlatformApi.DirectX9
};
foreach (var platformApi in allowedApis)
{
int dapi;
if (platformApi == AngleOptions.PlatformApi.DirectX9)
dapi = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
else if (platformApi == AngleOptions.PlatformApi.DirectX11)
dapi = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
else
continue;
_display = _egl.GetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, IntPtr.Zero,
new[] {EGL_PLATFORM_ANGLE_TYPE_ANGLE, dapi, EGL_NONE});
if (_display != IntPtr.Zero)
{
_angleApi = platformApi;
break;
}
}
if (_display == IntPtr.Zero)
throw new OpenGlException("Unable to create ANGLE display");
}
if (_display == IntPtr.Zero)
_display = _egl.GetDisplay(IntPtr.Zero);
} }
else else
{ {
if (_egl.GetPlatformDisplayEXT == null) if (egl.GetPlatformDisplayEXT == null)
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl"); throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl");
_display = _egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs); display = egl.GetPlatformDisplayEXT(platformType, platformDisplay, attrs);
} }
if (display == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglGetDisplay", egl);
return display;
}
if (_display == IntPtr.Zero) public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
throw OpenGlException.GetFormattedException("eglGetDisplay", _egl); : this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs))
{
}
public EglDisplay(EglInterface egl, IntPtr display)
{
_egl = egl;
_display = display;
if(_display == IntPtr.Zero)
throw new ArgumentException();
if (!_egl.Initialize(_display, out var major, out var minor)) if (!_egl.Initialize(_display, out var major, out var minor))
throw OpenGlException.GetFormattedException("eglInitialize", _egl); throw OpenGlException.GetFormattedException("eglInitialize", _egl);
@ -176,5 +156,15 @@ namespace Avalonia.OpenGL
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl); throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
return new EglSurface(this, _egl, s); return new EglSurface(this, _egl, s);
} }
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs)
{
var s = _egl.CreatePbufferFromClientBuffer(_display, bufferType, handle,
_config, attribs);
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", _egl);
return new EglSurface(this, _egl, s);
}
} }
} }

7
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@ -20,12 +20,13 @@ namespace Avalonia.OpenGL
if (feature != null) if (feature != null)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature); AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
} }
public static EglGlPlatformFeature TryCreate() public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay());
public static EglGlPlatformFeature TryCreate(Func<EglDisplay> displayFactory)
{ {
try try
{ {
var disp = new EglDisplay(); var disp = displayFactory();
return new EglGlPlatformFeature return new EglGlPlatformFeature
{ {
_display = disp, _display = disp,

84
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@ -3,33 +3,26 @@ using System.Threading;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL
{ {
public class EglGlPlatformSurface : IGlPlatformSurface public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{ {
public interface IEglWindowGlPlatformSurfaceInfo
{
IntPtr Handle { get; }
PixelSize Size { get; }
double Scaling { get; }
}
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly EglContext _context; private readonly EglContext _context;
private readonly IEglWindowGlPlatformSurfaceInfo _info; private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base()
{ {
_display = context.Display; _display = context.Display;
_context = context; _context = context;
_info = info; _info = info;
} }
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{ {
var glSurface = _display.CreateWindowSurface(_info.Handle); var glSurface = _display.CreateWindowSurface(_info.Handle);
return new RenderTarget(_display, _context, glSurface, _info); return new RenderTarget(_display, _context, glSurface, _info);
} }
class RenderTarget : IGlPlatformSurfaceRenderTargetWithCorruptionInfo class RenderTarget : EglPlatformSurfaceRenderTargetBase
{ {
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly EglContext _context; private readonly EglContext _context;
@ -38,7 +31,7 @@ namespace Avalonia.OpenGL
private PixelSize _initialSize; private PixelSize _initialSize;
public RenderTarget(EglDisplay display, EglContext context, public RenderTarget(EglDisplay display, EglContext context,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context)
{ {
_display = display; _display = display;
_context = context; _context = context;
@ -47,70 +40,11 @@ namespace Avalonia.OpenGL
_initialSize = info.Size; _initialSize = info.Size;
} }
public void Dispose() => _glSurface.Dispose(); public override void Dispose() => _glSurface.Dispose();
public bool IsCorrupted => _initialSize != _info.Size; public override bool IsCorrupted => _initialSize != _info.Size;
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
var l = _context.Lock();
try
{
if (IsCorrupted)
throw new RenderTargetCorruptedException();
var restoreContext = _context.MakeCurrent(_glSurface);
_display.EglInterface.WaitClient();
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
return new Session(_display, _context, _glSurface, _info, l, restoreContext);
}
catch
{
l.Dispose();
throw;
}
}
class Session : IGlPlatformSurfaceRenderingSession public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info);
{
private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglDisplay _display;
private IDisposable _lock;
private readonly IDisposable _restoreContext;
public Session(EglDisplay display, EglContext context,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable restoreContext)
{
_context = context;
_display = display;
_glSurface = glSurface;
_info = info;
_lock = @lock;
_restoreContext = restoreContext;
}
public void Dispose()
{
_context.GlInterface.Flush();
_display.EglInterface.WaitGL();
_glSurface.SwapBuffers();
_display.EglInterface.WaitClient();
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_restoreContext.Dispose();
_lock.Dispose();
}
public IGlContext Context => _context;
public PixelSize Size => _info.Size;
public double Scaling => _info.Scaling;
public bool IsYFlipped { get; }
}
} }
} }
} }

103
src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs

@ -0,0 +1,103 @@
using System;
namespace Avalonia.OpenGL
{
public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
{
public interface IEglWindowGlPlatformSurfaceInfo
{
IntPtr Handle { get; }
PixelSize Size { get; }
double Scaling { get; }
}
public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
}
public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
{
private readonly EglDisplay _display;
private readonly EglContext _context;
protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context)
{
_display = display;
_context = context;
}
public abstract bool IsCorrupted { get; }
public virtual void Dispose()
{
}
public abstract IGlPlatformSurfaceRenderingSession BeginDraw();
protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
{
var l = _context.Lock();
try
{
if (IsCorrupted)
throw new RenderTargetCorruptedException();
var restoreContext = _context.MakeCurrent(surface);
_display.EglInterface.WaitClient();
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped);
}
catch
{
l.Dispose();
throw;
}
}
class Session : IGlPlatformSurfaceRenderingSession
{
private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglDisplay _display;
private readonly IDisposable _lock;
private readonly IDisposable _restoreContext;
private readonly Action _onFinish;
public Session(EglDisplay display, EglContext context,
EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped)
{
IsYFlipped = isYFlipped;
_context = context;
_display = display;
_glSurface = glSurface;
_info = info;
_lock = @lock;
_restoreContext = restoreContext;
_onFinish = onFinish;
}
public void Dispose()
{
_context.GlInterface.Flush();
_display.EglInterface.WaitGL();
_glSurface.SwapBuffers();
_display.EglInterface.WaitClient();
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_restoreContext.Dispose();
_lock.Dispose();
_onFinish?.Invoke();
}
public IGlContext Context => _context;
public PixelSize Size => _info.Size;
public double Scaling => _info.Scaling;
public bool IsYFlipped { get; }
}
}
}

29
src/Avalonia.OpenGL/EglInterface.cs

@ -17,25 +17,21 @@ namespace Avalonia.OpenGL
} }
public EglInterface(Func<string, IntPtr> getProcAddress) : base(getProcAddress)
{
}
public EglInterface(string library) : base(Load(library)) public EglInterface(string library) : base(Load(library))
{ {
} }
[DllImport("libegl.dll", CharSet = CharSet.Ansi)]
static extern IntPtr eglGetProcAddress(string proc);
static Func<string, IntPtr> Load() static Func<string, IntPtr> Load()
{ {
var os = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem; var os = AvaloniaLocator.Current.GetService<IRuntimePlatform>().GetRuntimeInfo().OperatingSystem;
if(os == OperatingSystemType.Linux || os == OperatingSystemType.Android) if(os == OperatingSystemType.Linux || os == OperatingSystemType.Android)
return Load("libEGL.so.1"); return Load("libEGL.so.1");
if (os == OperatingSystemType.WinNT)
{
var disp = eglGetProcAddress("eglGetPlatformDisplayEXT");
if (disp == IntPtr.Zero)
throw new OpenGlException("libegl.dll doesn't have eglGetPlatformDisplayEXT entry point");
return eglGetProcAddress;
}
throw new PlatformNotSupportedException(); throw new PlatformNotSupportedException();
} }
@ -147,6 +143,21 @@ namespace Avalonia.OpenGL
return null; return null;
return Marshal.PtrToStringAnsi(rv); return Marshal.PtrToStringAnsi(rv);
} }
public delegate IntPtr EglCreatePbufferFromClientBuffer(IntPtr display, int buftype, IntPtr buffer, IntPtr config, int[] attrib_list);
[GlEntryPoint("eglCreatePbufferFromClientBuffer")]
public EglCreatePbufferFromClientBuffer CreatePbufferFromClientBuffer { get; }
public delegate bool EglQueryDisplayAttribEXT(IntPtr display, int attr, out IntPtr res);
[GlEntryPoint("eglQueryDisplayAttribEXT"), GlOptionalEntryPoint]
public EglQueryDisplayAttribEXT QueryDisplayAttribExt { get; }
public delegate bool EglQueryDeviceAttribEXT(IntPtr display, int attr, out IntPtr res);
[GlEntryPoint("eglQueryDeviceAttribEXT"), GlOptionalEntryPoint]
public EglQueryDisplayAttribEXT QueryDeviceAttribExt { get; }
// ReSharper restore UnassignedGetOnlyAutoProperty // ReSharper restore UnassignedGetOnlyAutoProperty
} }

3
src/Windows/Avalonia.Win32/Win32GlManager.cs

@ -1,4 +1,5 @@
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
@ -15,7 +16,7 @@ namespace Avalonia.Win32
{ {
if (!s_attemptedToInitialize) if (!s_attemptedToInitialize)
{ {
EglFeature = EglGlPlatformFeature.TryCreate(); EglFeature = EglGlPlatformFeature.TryCreate(() => new AngleWin32EglDisplay());
s_attemptedToInitialize = true; s_attemptedToInitialize = true;
} }

Loading…
Cancel
Save