136 changed files with 2869 additions and 2473 deletions
@ -1,32 +0,0 @@ |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.OpenGL.Egl; |
|||
using Avalonia.OpenGL.Surfaces; |
|||
|
|||
namespace Avalonia.Android.OpenGL |
|||
{ |
|||
internal sealed class GlPlatformSurface : EglGlPlatformSurfaceBase |
|||
{ |
|||
private readonly EglPlatformOpenGlInterface _egl; |
|||
private readonly IEglWindowGlPlatformSurfaceInfo _info; |
|||
|
|||
private GlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
_egl = egl; |
|||
_info = info; |
|||
} |
|||
|
|||
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() => |
|||
new GlRenderTarget(_egl, _info, _egl.CreateWindowSurface(_info.Handle), _info.Handle); |
|||
|
|||
public static GlPlatformSurface TryCreate(IEglWindowGlPlatformSurfaceInfo info) |
|||
{ |
|||
var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>(); |
|||
if (feature is EglPlatformOpenGlInterface egl) |
|||
{ |
|||
return new GlPlatformSurface(egl, info); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
using System; |
|||
|
|||
using Avalonia.OpenGL.Egl; |
|||
using Avalonia.OpenGL.Surfaces; |
|||
|
|||
namespace Avalonia.Android.OpenGL |
|||
{ |
|||
internal sealed class GlRenderTarget : EglPlatformSurfaceRenderTargetBase, IGlPlatformSurfaceRenderTargetWithCorruptionInfo |
|||
{ |
|||
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; |
|||
private readonly EglSurface _surface; |
|||
private readonly IntPtr _handle; |
|||
|
|||
public GlRenderTarget( |
|||
EglPlatformOpenGlInterface egl, |
|||
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, |
|||
EglSurface surface, |
|||
IntPtr handle) |
|||
: base(egl) |
|||
{ |
|||
_info = info; |
|||
_surface = surface; |
|||
_handle = handle; |
|||
} |
|||
|
|||
public bool IsCorrupted => _handle != _info.Handle; |
|||
|
|||
public override IGlPlatformSurfaceRenderingSession BeginDraw() => BeginDraw(_surface, _info); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Platform; |
|||
|
|||
public interface IOptionalFeatureProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Queries for an optional feature
|
|||
/// </summary>
|
|||
/// <param name="featureType">Feature type</param>
|
|||
public object? TryGetFeature(Type featureType); |
|||
} |
|||
|
|||
public static class OptionalFeatureProviderExtensions |
|||
{ |
|||
public static T? TryGetFeature<T>(this IOptionalFeatureProvider provider) where T : class => |
|||
(T?)provider.TryGetFeature(typeof(T)); |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Rendering; |
|||
|
|||
struct OwnedDisposable<T> :IDisposable where T : class, IDisposable |
|||
{ |
|||
private readonly bool _owns; |
|||
private T? _value; |
|||
|
|||
public T Value => _value ?? throw new ObjectDisposedException("OwnedDisposable"); |
|||
|
|||
public OwnedDisposable(T value, bool owns) |
|||
{ |
|||
_owns = owns; |
|||
_value = value; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if(_owns) |
|||
_value?.Dispose(); |
|||
_value = null; |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Disposables; |
|||
using Avalonia.Metadata; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Rendering; |
|||
|
|||
[Unstable] |
|||
// TODO: Make it internal once legacy renderers are removed
|
|||
public class PlatformRenderInterfaceContextManager |
|||
{ |
|||
private readonly IPlatformGraphics? _graphics; |
|||
private IPlatformRenderInterfaceContext? _backend; |
|||
private OwnedDisposable<IPlatformGraphicsContext>? _gpuContext; |
|||
|
|||
public PlatformRenderInterfaceContextManager(IPlatformGraphics? graphics) |
|||
{ |
|||
_graphics = graphics; |
|||
} |
|||
|
|||
public void EnsureValidBackendContext() |
|||
{ |
|||
if (_backend == null || _gpuContext?.Value.IsLost == true) |
|||
{ |
|||
_backend?.Dispose(); |
|||
_backend = null; |
|||
_gpuContext?.Dispose(); |
|||
_gpuContext = null; |
|||
|
|||
if (_graphics != null) |
|||
{ |
|||
if (_graphics.UsesSharedContext) |
|||
_gpuContext = new OwnedDisposable<IPlatformGraphicsContext>(_graphics.GetSharedContext(), false); |
|||
else |
|||
_gpuContext = new OwnedDisposable<IPlatformGraphicsContext>(_graphics.CreateContext(), true); |
|||
} |
|||
|
|||
_backend = AvaloniaLocator.Current.GetRequiredService<IPlatformRenderInterface>() |
|||
.CreateBackendContext(_gpuContext?.Value); |
|||
} |
|||
} |
|||
|
|||
public IPlatformRenderInterfaceContext Value |
|||
{ |
|||
get |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
return _backend!; |
|||
} |
|||
} |
|||
|
|||
public IDisposable EnsureCurrent() |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
if (_gpuContext.HasValue) |
|||
return _gpuContext.Value.Value.EnsureCurrent(); |
|||
return Disposable.Empty; |
|||
} |
|||
|
|||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) |
|||
{ |
|||
EnsureValidBackendContext(); |
|||
return _backend!.CreateRenderTarget(surfaces); |
|||
} |
|||
} |
|||
@ -1,95 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.OpenGL.Egl; |
|||
using static Avalonia.OpenGL.Egl.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.IsGetPlatformDisplayExtAvailable) |
|||
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, false, 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(EglPlatformOpenGlInterface egl, IntPtr handle) |
|||
{ |
|||
if (PlatformApi != AngleOptions.PlatformApi.DirectX11) |
|||
throw new InvalidOperationException("Current platform API is " + PlatformApi); |
|||
return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE }); |
|||
} |
|||
|
|||
public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle, int offsetX, int offsetY, int width, int height) |
|||
{ |
|||
if (PlatformApi != AngleOptions.PlatformApi.DirectX11) |
|||
throw new InvalidOperationException("Current platform API is " + PlatformApi); |
|||
return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_TEXTURE_OFFSET_X_ANGLE, offsetX, EGL_TEXTURE_OFFSET_Y_ANGLE, offsetY, EGL_NONE }); |
|||
} |
|||
} |
|||
} |
|||
@ -1,193 +1,176 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Threading; |
|||
using static Avalonia.OpenGL.Egl.EglConsts; |
|||
|
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public class EglDisplay |
|||
public class EglDisplay : IDisposable |
|||
{ |
|||
private readonly EglInterface _egl; |
|||
private IntPtr _display; |
|||
private readonly EglDisplayOptions _options; |
|||
private EglConfigInfo _config; |
|||
private bool _isLost; |
|||
private object _lock = new(); |
|||
|
|||
public bool SupportsSharing { get; } |
|||
private readonly IntPtr _display; |
|||
private readonly IntPtr _config; |
|||
private readonly int[] _contextAttributes; |
|||
private readonly int _surfaceType; |
|||
|
|||
public IntPtr Handle => _display; |
|||
public IntPtr Config => _config; |
|||
private int _sampleCount; |
|||
private int _stencilSize; |
|||
private GlVersion _version; |
|||
|
|||
public EglDisplay(EglInterface egl, bool supportsSharing) : this(egl, supportsSharing, -1, IntPtr.Zero, null) |
|||
public IntPtr Config => _config.Config; |
|||
internal bool SingleContext => !_options.SupportsMultipleContexts; |
|||
private List<EglContext> _contexts = new(); |
|||
|
|||
public EglDisplay() : this(new EglDisplayCreationOptions |
|||
{ |
|||
|
|||
} |
|||
|
|||
static IntPtr CreateDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs) |
|||
Egl = new EglInterface() |
|||
}) |
|||
{ |
|||
var display = IntPtr.Zero; |
|||
if (platformType == -1 && platformDisplay == IntPtr.Zero) |
|||
{ |
|||
if (display == IntPtr.Zero) |
|||
display = egl.GetDisplay(IntPtr.Zero); |
|||
} |
|||
else |
|||
{ |
|||
if (!egl.IsGetPlatformDisplayExtAvailable) |
|||
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl"); |
|||
display = egl.GetPlatformDisplayExt(platformType, platformDisplay, attrs); |
|||
} |
|||
|
|||
if (display == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglGetDisplay", egl); |
|||
return display; |
|||
|
|||
} |
|||
|
|||
public EglDisplay(EglInterface egl, bool supportsSharing, int platformType, IntPtr platformDisplay, int[] attrs) |
|||
: this(egl, supportsSharing, CreateDisplay(egl, platformType, platformDisplay, attrs)) |
|||
public EglDisplay(EglDisplayCreationOptions options) : this(EglDisplayUtils.CreateDisplay(options), options) |
|||
{ |
|||
|
|||
|
|||
} |
|||
|
|||
public EglDisplay(EglInterface egl, bool supportsSharing, IntPtr display) |
|||
|
|||
public EglDisplay(IntPtr display, EglDisplayOptions options) |
|||
{ |
|||
_egl = egl; |
|||
SupportsSharing = supportsSharing; |
|||
_egl = options.Egl; |
|||
SupportsSharing = options.SupportsContextSharing; |
|||
_display = display; |
|||
_options = options; |
|||
if(_display == IntPtr.Zero) |
|||
throw new ArgumentException(); |
|||
|
|||
|
|||
if (!_egl.Initialize(_display, out var major, out var minor)) |
|||
throw OpenGlException.GetFormattedException("eglInitialize", _egl); |
|||
|
|||
var glProfiles = AvaloniaLocator.Current.GetService<AngleOptions>()?.GlProfiles |
|||
?? new[] |
|||
{ |
|||
new GlVersion(GlProfileType.OpenGLES, 3, 0), |
|||
new GlVersion(GlProfileType.OpenGLES, 2, 0) |
|||
}; |
|||
|
|||
var cfgs = glProfiles.Select(x => |
|||
_config = EglDisplayUtils.InitializeAndGetConfig(_egl, display, options.GlVersions); |
|||
} |
|||
|
|||
public EglInterface EglInterface => _egl; |
|||
public EglContext CreateContext(EglContextOptions options) |
|||
{ |
|||
options ??= new EglContextOptions(); |
|||
lock (_lock) |
|||
{ |
|||
var typeBit = EGL_OPENGL_ES3_BIT; |
|||
var share = options.ShareWith; |
|||
if (share != null && !SupportsSharing) |
|||
throw new NotSupportedException("Context sharing is not supported by this display"); |
|||
|
|||
switch (x.Major) |
|||
{ |
|||
case 2: |
|||
typeBit = EGL_OPENGL_ES2_BIT; |
|||
break; |
|||
var offscreenSurface = options.OffscreenSurface; |
|||
|
|||
case 1: |
|||
typeBit = EGL_OPENGL_ES_BIT; |
|||
break; |
|||
} |
|||
|
|||
return new |
|||
if (offscreenSurface == null) |
|||
{ |
|||
Attributes = new[] |
|||
// Check if eglMakeCurrent can work with EGL_NONE as read-write surfaces
|
|||
var extensions = _egl.QueryString(Handle, EGL_EXTENSIONS); |
|||
if (extensions?.Contains("EGL_KHR_surfaceless_context") != true) |
|||
{ |
|||
EGL_CONTEXT_MAJOR_VERSION, x.Major, |
|||
EGL_CONTEXT_MINOR_VERSION, x.Minor, |
|||
EGL_NONE |
|||
}, |
|||
Api = EGL_OPENGL_ES_API, |
|||
RenderableTypeBit = typeBit, |
|||
Version = x |
|||
}; |
|||
}); |
|||
|
|||
foreach (var cfg in cfgs) |
|||
{ |
|||
if (!_egl.BindApi(cfg.Api)) |
|||
continue; |
|||
foreach(var surfaceType in new[]{EGL_PBUFFER_BIT|EGL_WINDOW_BIT, EGL_WINDOW_BIT}) |
|||
foreach(var stencilSize in new[]{8, 1, 0}) |
|||
foreach (var depthSize in new []{8, 1, 0}) |
|||
// Attempt to create a PBuffer as a surface for offscreen rendering
|
|||
if ((_config.SurfaceType | EGL_PBUFFER_BIT) == 0) |
|||
throw new InvalidOperationException( |
|||
"Platform doesn't support EGL_KHR_surfaceless_context and PBUFFER surfaces"); |
|||
|
|||
var pBufferSurface = _egl.CreatePBufferSurface(_display, Config, |
|||
new[] { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }); |
|||
if (pBufferSurface == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl); |
|||
|
|||
offscreenSurface = new EglSurface(this, pBufferSurface); |
|||
} |
|||
} |
|||
|
|||
var ctx = _egl.CreateContext(_display, Config, share?.Context ?? IntPtr.Zero, _config.Attributes); |
|||
if (ctx == IntPtr.Zero) |
|||
{ |
|||
var attribs = new[] |
|||
{ |
|||
EGL_SURFACE_TYPE, surfaceType, |
|||
EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit, |
|||
EGL_RED_SIZE, 8, |
|||
EGL_GREEN_SIZE, 8, |
|||
EGL_BLUE_SIZE, 8, |
|||
EGL_ALPHA_SIZE, 8, |
|||
EGL_STENCIL_SIZE, stencilSize, |
|||
EGL_DEPTH_SIZE, depthSize, |
|||
EGL_NONE |
|||
}; |
|||
if (!_egl.ChooseConfig(_display, attribs, out _config, 1, out int numConfigs)) |
|||
continue; |
|||
if (numConfigs == 0) |
|||
continue; |
|||
_contextAttributes = cfg.Attributes; |
|||
_surfaceType = surfaceType; |
|||
_version = cfg.Version; |
|||
_egl.GetConfigAttrib(_display, _config, EGL_SAMPLES, out _sampleCount); |
|||
_egl.GetConfigAttrib(_display, _config, EGL_STENCIL_SIZE, out _stencilSize); |
|||
goto Found; |
|||
var ex = OpenGlException.GetFormattedException("eglCreateContext", _egl); |
|||
offscreenSurface?.Dispose(); |
|||
throw ex; |
|||
} |
|||
|
|||
var rv = new EglContext(this, _egl, share, ctx, offscreenSurface, |
|||
_config.Version, _config.SampleCount, _config.StencilSize, |
|||
options.DisposeCallback, options.ExtraFeatures); |
|||
_contexts.Add(rv); |
|||
return rv; |
|||
} |
|||
Found: |
|||
if (_contextAttributes == null) |
|||
throw new OpenGlException("No suitable EGL config was found"); |
|||
} |
|||
|
|||
public EglDisplay() : this(false) |
|||
public EglSurface CreateWindowSurface(IntPtr window) |
|||
{ |
|||
|
|||
if (window == IntPtr.Zero) |
|||
throw new OpenGlException($"Window {window} is invalid."); |
|||
|
|||
using (Lock()) |
|||
{ |
|||
var s = EglInterface.CreateWindowSurface(Handle, Config, window, |
|||
new[] { EGL_NONE, EGL_NONE }); |
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", EglInterface); |
|||
return new EglSurface(this, s); |
|||
} |
|||
} |
|||
|
|||
public EglDisplay(bool supportsSharing) : this(new EglInterface(), supportsSharing) |
|||
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs) |
|||
{ |
|||
|
|||
using (Lock()) |
|||
{ |
|||
var s = EglInterface.CreatePbufferFromClientBuffer(Handle, bufferType, handle, |
|||
Config, attribs); |
|||
|
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", EglInterface); |
|||
return new EglSurface(this, s); |
|||
} |
|||
} |
|||
|
|||
protected virtual bool DisplayLockIsSharedWithContexts => false; |
|||
|
|||
public EglInterface EglInterface => _egl; |
|||
public EglContext CreateContext(IGlContext share) |
|||
internal object ContextSharedSyncRoot => DisplayLockIsSharedWithContexts ? _lock : null; |
|||
|
|||
internal void OnContextLost(EglContext context) |
|||
{ |
|||
if (share != null && !SupportsSharing) |
|||
throw new NotSupportedException("Context sharing is not supported by this display"); |
|||
|
|||
if((_surfaceType|EGL_PBUFFER_BIT) == 0) |
|||
throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces"); |
|||
var shareCtx = (EglContext)share; |
|||
var ctx = _egl.CreateContext(_display, _config, shareCtx?.Context ?? IntPtr.Zero, _contextAttributes); |
|||
if (ctx == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreateContext", _egl); |
|||
|
|||
var extensions = _egl.QueryString(Handle, EGL_EXTENSIONS); |
|||
|
|||
IntPtr surf = IntPtr.Zero; |
|||
if (extensions?.Contains("EGL_KHR_surfaceless_context") != true) |
|||
if (_options.ContextLossIsDisplayLoss) |
|||
_isLost = true; |
|||
} |
|||
|
|||
internal void OnContextDisposed(EglContext context) |
|||
{ |
|||
lock (_lock) |
|||
_contexts.Remove(context); |
|||
} |
|||
|
|||
public bool IsLost |
|||
{ |
|||
get |
|||
{ |
|||
surf = _egl.CreatePBufferSurface(_display, _config, |
|||
new[] { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }); |
|||
if (surf == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl); |
|||
if (_isLost || _display == IntPtr.Zero) |
|||
return true; |
|||
if (_options.DeviceLostCheckCallback?.Invoke() == true) |
|||
return _isLost = true; |
|||
return false; |
|||
} |
|||
|
|||
var rv = new EglContext(this, _egl, shareCtx, ctx, |
|||
context => |
|||
surf == IntPtr.Zero ? null : new EglSurface(this, context, surf), |
|||
_version, _sampleCount, _stencilSize); |
|||
return rv; |
|||
} |
|||
|
|||
public IDisposable Lock() |
|||
{ |
|||
Monitor.Enter(_lock); |
|||
return Disposable.Create(() => { Monitor.Exit(_lock); }); |
|||
} |
|||
|
|||
public EglContext CreateContext(EglContext share, EglSurface offscreenSurface) |
|||
public void Dispose() |
|||
{ |
|||
if (share != null && !SupportsSharing) |
|||
throw new NotSupportedException("Context sharing is not supported by this display"); |
|||
|
|||
var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes); |
|||
if (ctx == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreateContext", _egl); |
|||
var rv = new EglContext(this, _egl, share, ctx, _ => offscreenSurface, _version, _sampleCount, _stencilSize); |
|||
rv.MakeCurrent(null); |
|||
return rv; |
|||
lock (_lock) |
|||
{ |
|||
foreach(var ctx in _contexts) |
|||
ctx.Dispose(); |
|||
_contexts.Clear(); |
|||
if (_display != IntPtr.Zero) |
|||
_egl.Terminate(_display); |
|||
_display = IntPtr.Zero; |
|||
_config = null; |
|||
_options.DisposeCallback?.Invoke(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Avalonia.OpenGL.Egl; |
|||
|
|||
public class EglDisplayOptions |
|||
{ |
|||
public EglInterface Egl { get; set; } |
|||
public bool SupportsContextSharing { get; set; } |
|||
public bool SupportsMultipleContexts { get; set; } |
|||
public bool ContextLossIsDisplayLoss { get; set; } |
|||
public Func<bool> DeviceLostCheckCallback { get; set; } |
|||
public Action DisposeCallback { get; set; } |
|||
public IEnumerable<GlVersion> GlVersions { get; set; } |
|||
} |
|||
|
|||
public class EglContextOptions |
|||
{ |
|||
public EglContext ShareWith { get; set; } |
|||
public EglSurface OffscreenSurface { get; set; } |
|||
public Action DisposeCallback { get; set; } |
|||
public Dictionary<Type, object> ExtraFeatures { get; set; } |
|||
} |
|||
|
|||
public class EglDisplayCreationOptions : EglDisplayOptions |
|||
{ |
|||
public int? PlatformType { get; set; } |
|||
public IntPtr PlatformDisplay { get; set; } |
|||
public int[] PlatformDisplayAttrs { get; set; } |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Reactive.Disposables; |
|||
using System.Threading; |
|||
using static Avalonia.OpenGL.Egl.EglConsts; |
|||
namespace Avalonia.OpenGL.Egl; |
|||
|
|||
static class EglDisplayUtils |
|||
{ |
|||
public static IntPtr CreateDisplay(EglDisplayCreationOptions options) |
|||
{ |
|||
var egl = options.Egl; |
|||
var display = IntPtr.Zero; |
|||
if (options.PlatformType == null) |
|||
{ |
|||
if (display == IntPtr.Zero) |
|||
display = egl.GetDisplay(IntPtr.Zero); |
|||
} |
|||
else |
|||
{ |
|||
if (!egl.IsGetPlatformDisplayExtAvailable) |
|||
throw new OpenGlException("eglGetPlatformDisplayEXT is not supported by libegl"); |
|||
|
|||
display = egl.GetPlatformDisplayExt(options.PlatformType.Value, options.PlatformDisplay, |
|||
options.PlatformDisplayAttrs); |
|||
} |
|||
|
|||
if (display == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglGetDisplay", egl); |
|||
return display; |
|||
} |
|||
|
|||
public static EglConfigInfo InitializeAndGetConfig(EglInterface egl, IntPtr display, IEnumerable<GlVersion> versions) |
|||
{ |
|||
if (!egl.Initialize(display, out var major, out var minor)) |
|||
throw OpenGlException.GetFormattedException("eglInitialize", egl); |
|||
|
|||
// TODO: AvaloniaLocator.Current.GetService<AngleOptions>()?.GlProfiles
|
|||
versions ??= new[] |
|||
{ |
|||
new GlVersion(GlProfileType.OpenGLES, 3, 0), |
|||
new GlVersion(GlProfileType.OpenGLES, 2, 0) |
|||
}; |
|||
|
|||
var cfgs = versions |
|||
.Where(x => x.Type == GlProfileType.OpenGLES) |
|||
.Select(x => |
|||
{ |
|||
var typeBit = EGL_OPENGL_ES3_BIT; |
|||
|
|||
switch (x.Major) |
|||
{ |
|||
case 2: |
|||
typeBit = EGL_OPENGL_ES2_BIT; |
|||
break; |
|||
|
|||
case 1: |
|||
typeBit = EGL_OPENGL_ES_BIT; |
|||
break; |
|||
} |
|||
|
|||
return new |
|||
{ |
|||
Attributes = new[] |
|||
{ |
|||
EGL_CONTEXT_MAJOR_VERSION, x.Major, |
|||
EGL_CONTEXT_MINOR_VERSION, x.Minor, |
|||
EGL_NONE |
|||
}, |
|||
Api = EGL_OPENGL_ES_API, |
|||
RenderableTypeBit = typeBit, |
|||
Version = x |
|||
}; |
|||
}); |
|||
|
|||
foreach (var cfg in cfgs) |
|||
{ |
|||
if (!egl.BindApi(cfg.Api)) |
|||
continue; |
|||
foreach (var surfaceType in new[] { EGL_PBUFFER_BIT | EGL_WINDOW_BIT, EGL_WINDOW_BIT }) |
|||
foreach (var stencilSize in new[] { 8, 1, 0 }) |
|||
foreach (var depthSize in new[] { 8, 1, 0 }) |
|||
{ |
|||
var attribs = new[] |
|||
{ |
|||
EGL_SURFACE_TYPE, surfaceType, |
|||
EGL_RENDERABLE_TYPE, cfg.RenderableTypeBit, |
|||
EGL_RED_SIZE, 8, |
|||
EGL_GREEN_SIZE, 8, |
|||
EGL_BLUE_SIZE, 8, |
|||
EGL_ALPHA_SIZE, 8, |
|||
EGL_STENCIL_SIZE, stencilSize, |
|||
EGL_DEPTH_SIZE, depthSize, |
|||
EGL_NONE |
|||
}; |
|||
if (!egl.ChooseConfig(display, attribs, out var config, 1, out int numConfigs)) |
|||
continue; |
|||
if (numConfigs == 0) |
|||
continue; |
|||
|
|||
|
|||
egl.GetConfigAttrib(display, config, EGL_SAMPLES, out var sampleCount); |
|||
egl.GetConfigAttrib(display, config, EGL_STENCIL_SIZE, out var returnedStencilSize); |
|||
return new EglConfigInfo(config, cfg.Version, surfaceType, cfg.Attributes, sampleCount, |
|||
returnedStencilSize); |
|||
} |
|||
} |
|||
|
|||
throw new OpenGlException("No suitable EGL config was found"); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
class EglConfigInfo |
|||
{ |
|||
public IntPtr Config { get; } |
|||
public GlVersion Version { get; } |
|||
public int SurfaceType { get; } |
|||
public int[] Attributes { get; } |
|||
public int SampleCount { get; } |
|||
public int StencilSize { get; } |
|||
|
|||
public EglConfigInfo(IntPtr config, GlVersion version, int surfaceType, int[] attributes, int sampleCount, |
|||
int stencilSize) |
|||
{ |
|||
Config = config; |
|||
Version = version; |
|||
SurfaceType = surfaceType; |
|||
Attributes = attributes; |
|||
SampleCount = sampleCount; |
|||
StencilSize = stencilSize; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Platform; |
|||
using static Avalonia.OpenGL.Egl.EglConsts; |
|||
|
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public sealed class EglPlatformGraphics : IPlatformGraphics |
|||
{ |
|||
private readonly EglDisplay _display; |
|||
public bool UsesSharedContext => false; |
|||
public IPlatformGraphicsContext CreateContext() => _display.CreateContext(null); |
|||
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); |
|||
|
|||
public EglPlatformGraphics(EglDisplay display) |
|||
{ |
|||
_display = display; |
|||
} |
|||
|
|||
public static void TryInitialize() |
|||
{ |
|||
var feature = TryCreate(); |
|||
if (feature != null) |
|||
AvaloniaLocator.CurrentMutable.Bind<IPlatformGraphics>().ToConstant(feature); |
|||
} |
|||
|
|||
public static EglPlatformGraphics TryCreate() => TryCreate(() => new EglDisplay(new EglDisplayCreationOptions |
|||
{ |
|||
Egl = new EglInterface(), |
|||
// Those are expected to be supported by most EGL implementations
|
|||
SupportsMultipleContexts = true, |
|||
SupportsContextSharing = true |
|||
})); |
|||
|
|||
public static EglPlatformGraphics TryCreate(Func<EglDisplay> displayFactory) |
|||
{ |
|||
try |
|||
{ |
|||
return new EglPlatformGraphics(displayFactory()); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,77 +0,0 @@ |
|||
using System; |
|||
using Avalonia.Logging; |
|||
using Avalonia.Platform; |
|||
using static Avalonia.OpenGL.Egl.EglConsts; |
|||
|
|||
namespace Avalonia.OpenGL.Egl |
|||
{ |
|||
public class EglPlatformOpenGlInterface : IPlatformOpenGlInterface |
|||
{ |
|||
public EglDisplay Display { get; private set; } |
|||
public bool CanCreateContexts => true; |
|||
public bool CanShareContexts => Display.SupportsSharing; |
|||
|
|||
public EglContext PrimaryEglContext { get; } |
|||
public IGlContext PrimaryContext => PrimaryEglContext; |
|||
IPlatformGpuContext IPlatformGpu.PrimaryContext => PrimaryContext; |
|||
|
|||
public EglPlatformOpenGlInterface(EglDisplay display) |
|||
{ |
|||
Display = display; |
|||
PrimaryEglContext = display.CreateContext(null); |
|||
} |
|||
|
|||
public static void TryInitialize() |
|||
{ |
|||
var feature = TryCreate(); |
|||
if (feature != null) |
|||
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature); |
|||
} |
|||
|
|||
public static EglPlatformOpenGlInterface TryCreate() => TryCreate(() => new EglDisplay()); |
|||
public static EglPlatformOpenGlInterface TryCreate(Func<EglDisplay> displayFactory) |
|||
{ |
|||
try |
|||
{ |
|||
return new EglPlatformOpenGlInterface(displayFactory()); |
|||
} |
|||
catch(Exception e) |
|||
{ |
|||
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public IGlContext CreateContext() => Display.CreateContext(null); |
|||
public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryEglContext); |
|||
|
|||
|
|||
public EglSurface CreateWindowSurface(IntPtr window) |
|||
{ |
|||
if (window == IntPtr.Zero) |
|||
throw new OpenGlException($"Window {window} is invalid."); |
|||
|
|||
using (PrimaryContext.MakeCurrent()) |
|||
{ |
|||
var s = Display.EglInterface.CreateWindowSurface(Display.Handle, Display.Config, window, |
|||
new[] { EGL_NONE, EGL_NONE }); |
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", Display.EglInterface); |
|||
return new EglSurface(Display, PrimaryEglContext, s); |
|||
} |
|||
} |
|||
|
|||
public EglSurface CreatePBufferFromClientBuffer (int bufferType, IntPtr handle, int[] attribs) |
|||
{ |
|||
using (PrimaryContext.MakeCurrent()) |
|||
{ |
|||
var s = Display.EglInterface.CreatePbufferFromClientBuffer(Display.Handle, bufferType, handle, |
|||
Display.Config, attribs); |
|||
|
|||
if (s == IntPtr.Zero) |
|||
throw OpenGlException.GetFormattedException("eglCreatePbufferFromClientBuffer", Display.EglInterface); |
|||
return new EglSurface(Display, PrimaryEglContext, s); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
using Avalonia.OpenGL.Imaging; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IOpenGlAwarePlatformRenderInterface |
|||
{ |
|||
IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.OpenGL.Imaging; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IOpenGlTextureSharingRenderInterfaceContextFeature |
|||
{ |
|||
bool CanCreateSharedContext { get; } |
|||
IGlContext CreateSharedContext(IEnumerable<GlVersion> preferredVersions = null); |
|||
IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi); |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
{ |
|||
public interface IPlatformOpenGlInterface : IPlatformGpu |
|||
{ |
|||
new IGlContext PrimaryContext { get; } |
|||
IGlContext CreateSharedContext(); |
|||
bool CanShareContexts { get; } |
|||
bool CanCreateContexts { get; } |
|||
IGlContext CreateContext(); |
|||
/*IGlContext TryCreateContext(GlVersion version); |
|||
*/ |
|||
} |
|||
} |
|||
@ -1,9 +1,10 @@ |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.LinuxFramebuffer.Output |
|||
{ |
|||
public interface IGlOutputBackend : IOutputBackend |
|||
{ |
|||
public IPlatformOpenGlInterface PlatformOpenGlInterface { get; } |
|||
public IPlatformGraphics PlatformGraphics { get; } |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls.Platform.Surfaces; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Skia; |
|||
|
|||
internal class SkiaContext : IPlatformRenderInterfaceContext |
|||
{ |
|||
private ISkiaGpu _gpu; |
|||
|
|||
public SkiaContext(ISkiaGpu gpu) |
|||
{ |
|||
_gpu = gpu; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_gpu?.Dispose(); |
|||
_gpu = null; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) |
|||
{ |
|||
if (!(surfaces is IList)) |
|||
surfaces = surfaces.ToList(); |
|||
var gpuRenderTarget = _gpu?.TryCreateRenderTarget(surfaces); |
|||
if (gpuRenderTarget != null) |
|||
{ |
|||
return new SkiaGpuRenderTarget(_gpu, gpuRenderTarget); |
|||
} |
|||
|
|||
foreach (var surface in surfaces) |
|||
{ |
|||
if (surface is IFramebufferPlatformSurface framebufferSurface) |
|||
return new FramebufferRenderTarget(framebufferSurface); |
|||
} |
|||
|
|||
throw new NotSupportedException( |
|||
"Don't know how to create a Skia render target from any of provided surfaces"); |
|||
} |
|||
|
|||
public object TryGetFeature(Type featureType) => _gpu?.TryGetFeature(featureType); |
|||
} |
|||
@ -1,6 +1,7 @@ |
|||
using System.Collections.Generic; |
|||
using Avalonia.OpenGL; |
|||
|
|||
namespace Avalonia.OpenGL |
|||
namespace Avalonia.Win32 |
|||
{ |
|||
public class AngleOptions |
|||
{ |
|||
File diff suppressed because it is too large
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using Avalonia.OpenGL; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Win32.DirectX; |
|||
|
|||
public interface IDirect3D11TexturePlatformSurface |
|||
{ |
|||
public IDirect3D11TextureRenderTarget CreateRenderTarget(IPlatformGraphicsContext graphicsContext, IntPtr d3dDevice); |
|||
} |
|||
|
|||
|
|||
|
|||
public interface IDirect3D11TextureRenderTarget : IDisposable |
|||
{ |
|||
bool IsCorrupted { get; } |
|||
IDirect3D11TextureRenderTargetRenderSession BeginDraw(); |
|||
} |
|||
|
|||
public interface IDirect3D11TextureRenderTargetRenderSession : IDisposable |
|||
{ |
|||
public IntPtr D3D11Texture2D { get; } |
|||
public PixelSize Size { get; } |
|||
public PixelPoint Offset { get; } |
|||
public double Scaling { get; } |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue