Browse Source

Merge branch 'master' into render-inside-win-ui-comp-tree

# Conflicts:
#	src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
#	src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
#	src/Windows/Avalonia.Win32/WindowImpl.cs
tmp-winuicomp_with_manual_fbo
Dan Walmsley 6 years ago
parent
commit
bb767fd08d
  1. 1
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  2. 3
      src/Avalonia.Base/ApiCompatBaseline.txt
  3. 17
      src/Avalonia.Base/AvaloniaProperty.cs
  4. 48
      src/Avalonia.Base/AvaloniaProperty`1.cs
  5. 17
      src/Avalonia.Base/DirectPropertyBase.cs
  6. 4
      src/Avalonia.Controls/Mixins/SelectableMixin.cs
  7. 2
      src/Avalonia.Controls/NativeMenu.Export.cs
  8. 2
      src/Avalonia.Controls/NativeMenuItem.cs
  9. 2
      src/Avalonia.Controls/Presenters/TextPresenter.cs
  10. 3
      src/Avalonia.Controls/Primitives/IPopupHost.cs
  11. 2
      src/Avalonia.Controls/TextBlock.cs
  12. 8
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  13. 41
      src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs
  14. 6
      src/Avalonia.Native/PopupImpl.cs
  15. 6
      src/Avalonia.Native/WindowImpl.cs
  16. 2
      src/Avalonia.Native/WindowImplBase.cs
  17. 1
      src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
  18. 14
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  19. 192
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  20. 2
      src/Avalonia.OpenGL/Egl/EglConsts.cs
  21. 69
      src/Avalonia.OpenGL/Egl/EglContext.cs
  22. 53
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  23. 2
      src/Avalonia.OpenGL/Egl/EglErrors.cs
  24. 54
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs
  25. 47
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs
  26. 2
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  27. 72
      src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs
  28. 11
      src/Avalonia.OpenGL/Egl/EglSurface.cs
  29. 43
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  30. 51
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  31. 13
      src/Avalonia.OpenGL/GlInterface.cs
  32. 2
      src/Avalonia.OpenGL/IGlContext.cs
  33. 2
      src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs
  34. 13
      src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs
  35. 8
      src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs
  36. 17
      src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs
  37. 13
      src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs
  38. 34
      src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs
  39. 1
      src/Avalonia.OpenGL/OpenGlException.cs
  40. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs
  41. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs
  42. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs
  43. 53
      src/Avalonia.X11/Glx/GlxContext.cs
  44. 8
      src/Avalonia.X11/Glx/GlxDisplay.cs
  45. 25
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  46. 13
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  47. 5
      src/Avalonia.X11/X11Platform.cs
  48. 9
      src/Avalonia.X11/X11Window.cs
  49. 4
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  50. 18
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  51. 9
      src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
  52. 2
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  53. 1
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
  54. 9
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  55. 207
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  56. 81
      src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs
  57. 6
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  58. 29
      src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs
  59. 2
      src/Windows/Avalonia.Win32/Composition/CompositionHost.cs
  60. 9
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  61. 11
      src/Windows/Avalonia.Win32/WindowImpl.cs
  62. 20
      src/iOS/Avalonia.iOS/EaglDisplay.cs
  63. 3
      src/iOS/Avalonia.iOS/EaglLayerSurface.cs
  64. 2
      src/iOS/Avalonia.iOS/Platform.cs
  65. 4
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs
  66. 4
      tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs
  67. 62
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

1
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
using Avalonia.Threading; using Avalonia.Threading;
using static Avalonia.OpenGL.GlConsts; using static Avalonia.OpenGL.GlConsts;

3
src/Avalonia.Base/ApiCompatBaseline.txt

@ -0,0 +1,3 @@
Compat issues with assembly Avalonia.Base:
CannotAddAbstractMembers : Member 'protected System.IObservable<Avalonia.AvaloniaPropertyChangedEventArgs> Avalonia.AvaloniaProperty.GetChanged()' is abstract in the implementation but is missing in the contract.
Total Issues: 1

17
src/Avalonia.Base/AvaloniaProperty.cs

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -18,7 +17,6 @@ namespace Avalonia
public static readonly object UnsetValue = new UnsetValueType(); public static readonly object UnsetValue = new UnsetValueType();
private static int s_nextId; private static int s_nextId;
private readonly Subject<AvaloniaPropertyChangedEventArgs> _changed;
private readonly PropertyMetadata _defaultMetadata; private readonly PropertyMetadata _defaultMetadata;
private readonly Dictionary<Type, PropertyMetadata> _metadata; private readonly Dictionary<Type, PropertyMetadata> _metadata;
private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>(); private readonly Dictionary<Type, PropertyMetadata> _metadataCache = new Dictionary<Type, PropertyMetadata>();
@ -50,7 +48,6 @@ namespace Avalonia
throw new ArgumentException("'name' may not contain periods."); throw new ArgumentException("'name' may not contain periods.");
} }
_changed = new Subject<AvaloniaPropertyChangedEventArgs>();
_metadata = new Dictionary<Type, PropertyMetadata>(); _metadata = new Dictionary<Type, PropertyMetadata>();
Name = name; Name = name;
@ -77,7 +74,6 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(source != null);
Contract.Requires<ArgumentNullException>(ownerType != null); Contract.Requires<ArgumentNullException>(ownerType != null);
_changed = source._changed;
_metadata = new Dictionary<Type, PropertyMetadata>(); _metadata = new Dictionary<Type, PropertyMetadata>();
Name = source.Name; Name = source.Name;
@ -139,7 +135,7 @@ namespace Avalonia
/// An observable that is fired when this property changes on any /// An observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance. /// <see cref="AvaloniaObject"/> instance.
/// </value> /// </value>
public IObservable<AvaloniaPropertyChangedEventArgs> Changed => _changed; public IObservable<AvaloniaPropertyChangedEventArgs> Changed => GetChanged();
/// <summary> /// <summary>
/// Gets a method that gets called before and after the property starts being notified on an /// Gets a method that gets called before and after the property starts being notified on an
@ -474,15 +470,6 @@ namespace Avalonia
public abstract void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data) public abstract void Accept<TData>(IAvaloniaPropertyVisitor<TData> vistor, ref TData data)
where TData : struct; where TData : struct;
/// <summary>
/// Notifies the <see cref="Changed"/> observable.
/// </summary>
/// <param name="e">The observable arguments.</param>
internal void NotifyChanged(AvaloniaPropertyChangedEventArgs e)
{
_changed.OnNext(e);
}
/// <summary> /// <summary>
/// Routes an untyped ClearValue call to a typed call. /// Routes an untyped ClearValue call to a typed call.
/// </summary> /// </summary>
@ -553,6 +540,8 @@ namespace Avalonia
_hasMetadataOverrides = true; _hasMetadataOverrides = true;
} }
protected abstract IObservable<AvaloniaPropertyChangedEventArgs> GetChanged();
private PropertyMetadata GetMetadataWithOverrides(Type type) private PropertyMetadata GetMetadataWithOverrides(Type type)
{ {
if (type is null) if (type is null)

48
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -10,6 +11,8 @@ namespace Avalonia
/// <typeparam name="TValue">The value type of the property.</typeparam> /// <typeparam name="TValue">The value type of the property.</typeparam>
public abstract class AvaloniaProperty<TValue> : AvaloniaProperty public abstract class AvaloniaProperty<TValue> : AvaloniaProperty
{ {
private readonly Subject<AvaloniaPropertyChangedEventArgs<TValue>> _changed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary> /// </summary>
@ -24,22 +27,61 @@ namespace Avalonia
Action<IAvaloniaObject, bool> notifying = null) Action<IAvaloniaObject, bool> notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying) : base(name, typeof(TValue), ownerType, metadata, notifying)
{ {
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary> /// </summary>
/// <param name="source">The property to copy.</param> /// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param> /// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param> /// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with AvaloniaProperty<TValue> instead.", true)]
protected AvaloniaProperty( protected AvaloniaProperty(
AvaloniaProperty source, AvaloniaProperty source,
Type ownerType, Type ownerType,
PropertyMetadata metadata)
: this(source as AvaloniaProperty<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected AvaloniaProperty(
AvaloniaProperty<TValue> source,
Type ownerType,
PropertyMetadata metadata) PropertyMetadata metadata)
: base(source, ownerType, metadata) : base(source, ownerType, metadata)
{ {
_changed = source._changed;
}
/// <summary>
/// Gets an observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance.
/// </summary>
/// <value>
/// An observable that is fired when this property changes on any
/// <see cref="AvaloniaObject"/> instance.
/// </value>
public new IObservable<AvaloniaPropertyChangedEventArgs<TValue>> Changed => _changed;
/// <summary>
/// Notifies the <see cref="Changed"/> observable.
/// </summary>
/// <param name="e">The observable arguments.</param>
internal void NotifyChanged(AvaloniaPropertyChangedEventArgs<TValue> e)
{
_changed.OnNext(e);
} }
protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
protected BindingValue<object> TryConvert(object value) protected BindingValue<object> TryConvert(object value)
{ {
if (value == UnsetValue) if (value == UnsetValue)

17
src/Avalonia.Base/DirectPropertyBase.cs

@ -32,15 +32,30 @@ namespace Avalonia
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AvaloniaProperty"/> class. /// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary> /// </summary>
/// <param name="source">The property to copy.</param> /// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param> /// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param> /// <param name="metadata">Optional overridden metadata.</param>
[Obsolete("Use constructor with DirectPropertyBase<TValue> instead.", true)]
protected DirectPropertyBase( protected DirectPropertyBase(
AvaloniaProperty source, AvaloniaProperty source,
Type ownerType, Type ownerType,
PropertyMetadata metadata) PropertyMetadata metadata)
: this(source as DirectPropertyBase<TValue> ?? throw new InvalidOperationException(), ownerType, metadata)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DirectPropertyBase{TValue}"/> class.
/// </summary>
/// <param name="source">The property to copy.</param>
/// <param name="ownerType">The new owner type.</param>
/// <param name="metadata">Optional overridden metadata.</param>
protected DirectPropertyBase(
DirectPropertyBase<TValue> source,
Type ownerType,
PropertyMetadata metadata)
: base(source, ownerType, metadata) : base(source, ownerType, metadata)
{ {
} }

4
src/Avalonia.Controls/Mixins/SelectableMixin.cs

@ -48,7 +48,7 @@ namespace Avalonia.Controls.Mixins
if (sender != null) if (sender != null)
{ {
((IPseudoClasses)sender.Classes).Set(":selected", (bool)x.NewValue); ((IPseudoClasses)sender.Classes).Set(":selected", x.NewValue.GetValueOrDefault());
sender.RaiseEvent(new RoutedEventArgs sender.RaiseEvent(new RoutedEventArgs
{ {
@ -58,4 +58,4 @@ namespace Avalonia.Controls.Mixins
}); });
} }
} }
} }

2
src/Avalonia.Controls/NativeMenu.Export.cs

@ -77,7 +77,7 @@ namespace Avalonia.Controls
{ {
if (args.Sender is TopLevel tl) if (args.Sender is TopLevel tl)
{ {
GetInfo(tl).Exporter?.SetNativeMenu((NativeMenu)args.NewValue); GetInfo(tl).Exporter?.SetNativeMenu(args.NewValue.GetValueOrDefault());
} }
}); });
} }

2
src/Avalonia.Controls/NativeMenuItem.cs

@ -23,7 +23,7 @@ namespace Avalonia.Controls
MenuProperty.Changed.Subscribe(args => MenuProperty.Changed.Subscribe(args =>
{ {
var item = (NativeMenuItem)args.Sender; var item = (NativeMenuItem)args.Sender;
var value = (NativeMenu)args.NewValue; var value = args.NewValue.GetValueOrDefault();
if (value.Parent != null && value.Parent != item) if (value.Parent != null && value.Parent != item)
throw new InvalidOperationException("NativeMenu already has a parent"); throw new InvalidOperationException("NativeMenu already has a parent");
value.Parent = item; value.Parent = item;

2
src/Avalonia.Controls/Presenters/TextPresenter.cs

@ -82,7 +82,7 @@ namespace Avalonia.Controls.Presenters
TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty, TextAlignmentProperty, TextWrappingProperty, TextBlock.FontSizeProperty,
TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty); TextBlock.FontStyleProperty, TextBlock.FontWeightProperty, TextBlock.FontFamilyProperty);
Observable.Merge(TextProperty.Changed, TextBlock.ForegroundProperty.Changed, Observable.Merge<AvaloniaPropertyChangedEventArgs>(TextProperty.Changed, TextBlock.ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextWrappingProperty.Changed, TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed, TextBlock.FontSizeProperty.Changed, TextBlock.FontStyleProperty.Changed,
TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed, TextBlock.FontWeightProperty.Changed, TextBlock.FontFamilyProperty.Changed,

3
src/Avalonia.Controls/Primitives/IPopupHost.cs

@ -1,6 +1,7 @@
using System; using System;
using Avalonia.Controls.Presenters; using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives.PopupPositioning; using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Input;
using Avalonia.VisualTree; using Avalonia.VisualTree;
namespace Avalonia.Controls.Primitives namespace Avalonia.Controls.Primitives
@ -13,7 +14,7 @@ namespace Avalonia.Controls.Primitives
/// (<see cref="PopupRoot"/>) or an <see cref="OverlayPopupHost"/> which is created /// (<see cref="PopupRoot"/>) or an <see cref="OverlayPopupHost"/> which is created
/// on an <see cref="OverlayLayer"/>. /// on an <see cref="OverlayLayer"/>.
/// </remarks> /// </remarks>
public interface IPopupHost : IDisposable public interface IPopupHost : IDisposable, IFocusScope
{ {
/// <summary> /// <summary>
/// Sets the control to display in the popup. /// Sets the control to display in the popup.

2
src/Avalonia.Controls/TextBlock.cs

@ -138,7 +138,7 @@ namespace Avalonia.Controls
FontStyleProperty, TextWrappingProperty, FontFamilyProperty, FontStyleProperty, TextWrappingProperty, FontFamilyProperty,
TextTrimmingProperty, TextProperty, PaddingProperty, LineHeightProperty, MaxLinesProperty); TextTrimmingProperty, TextProperty, PaddingProperty, LineHeightProperty, MaxLinesProperty);
Observable.Merge(TextProperty.Changed, ForegroundProperty.Changed, Observable.Merge<AvaloniaPropertyChangedEventArgs>(TextProperty.Changed, ForegroundProperty.Changed,
TextAlignmentProperty.Changed, TextWrappingProperty.Changed, TextAlignmentProperty.Changed, TextWrappingProperty.Changed,
TextTrimmingProperty.Changed, FontSizeProperty.Changed, TextTrimmingProperty.Changed, FontSizeProperty.Changed,
FontStyleProperty.Changed, FontWeightProperty.Changed, FontStyleProperty.Changed, FontWeightProperty.Changed,

8
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -16,7 +16,7 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private AvaloniaNativePlatformOptions _options; private AvaloniaNativePlatformOptions _options;
private GlPlatformFeature _glFeature; private AvaloniaNativePlatformOpenGlInterface _platformGl;
[DllImport("libAvaloniaNative")] [DllImport("libAvaloniaNative")]
static extern IntPtr CreateAvaloniaNative(); static extern IntPtr CreateAvaloniaNative();
@ -116,8 +116,8 @@ namespace Avalonia.Native
{ {
try try
{ {
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>() AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>()
.ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay())); .ToConstant(_platformGl = new AvaloniaNativePlatformOpenGlInterface(_factory.ObtainGlDisplay()));
} }
catch (Exception) catch (Exception)
{ {
@ -128,7 +128,7 @@ namespace Avalonia.Native
public IWindowImpl CreateWindow() public IWindowImpl CreateWindow()
{ {
return new WindowImpl(_factory, _options, _glFeature); return new WindowImpl(_factory, _options, _platformGl);
} }
public IWindowImpl CreateEmbeddableWindow() public IWindowImpl CreateEmbeddableWindow()

41
src/Avalonia.Native/GlPlatformFeature.cs → src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs

@ -2,21 +2,20 @@
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.Native.Interop; using Avalonia.Native.Interop;
using System.Drawing; using System.Drawing;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Threading; using Avalonia.Threading;
namespace Avalonia.Native namespace Avalonia.Native
{ {
class GlPlatformFeature : IWindowingPlatformGlFeature class AvaloniaNativePlatformOpenGlInterface : IPlatformOpenGlInterface
{ {
private readonly IAvnGlDisplay _display; private readonly IAvnGlDisplay _display;
public GlPlatformFeature(IAvnGlDisplay display) public AvaloniaNativePlatformOpenGlInterface(IAvnGlDisplay display)
{ {
_display = display; _display = display;
var immediate = display.CreateContext(null); var immediate = display.CreateContext(null);
var deferred = display.CreateContext(immediate);
int major, minor; int major, minor;
GlInterface glInterface; GlInterface glInterface;
using (immediate.MakeCurrent()) using (immediate.MakeCurrent())
@ -33,19 +32,22 @@ namespace Avalonia.Native
} }
GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize); GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize);
MainContext = new GlContext(GlDisplay, null, immediate, _version);
ImmediateContext = new GlContext(GlDisplay, immediate, _version);
DeferredContext = new GlContext(GlDisplay, deferred, _version);
} }
internal IGlContext ImmediateContext { get; } internal GlContext MainContext { get; }
public IGlContext MainContext => DeferredContext; public IGlContext PrimaryContext => MainContext;
internal GlContext DeferredContext { get; }
public bool CanShareContexts => true;
public bool CanCreateContexts => true;
internal GlDisplay GlDisplay; internal GlDisplay GlDisplay;
private readonly GlVersion _version; private readonly GlVersion _version;
public IGlContext CreateSharedContext() => new GlContext(GlDisplay,
MainContext, _display.CreateContext(MainContext.Context), _version);
public IGlContext CreateContext() => new GlContext(GlDisplay, public IGlContext CreateContext() => new GlContext(GlDisplay,
_display.CreateContext(((GlContext)ImmediateContext).Context), _version); null, _display.CreateContext(null), _version);
} }
class GlDisplay class GlDisplay
@ -72,11 +74,13 @@ namespace Avalonia.Native
class GlContext : IGlContext class GlContext : IGlContext
{ {
private readonly GlDisplay _display; private readonly GlDisplay _display;
private readonly GlContext _sharedWith;
public IAvnGlContext Context { get; private set; } public IAvnGlContext Context { get; private set; }
public GlContext(GlDisplay display, IAvnGlContext context, GlVersion version) public GlContext(GlDisplay display, GlContext sharedWith, IAvnGlContext context, GlVersion version)
{ {
_display = display; _display = display;
_sharedWith = sharedWith;
Context = context; Context = context;
Version = version; Version = version;
} }
@ -86,6 +90,17 @@ namespace Avalonia.Native
public int SampleCount => _display.SampleCount; public int SampleCount => _display.SampleCount;
public int StencilSize => _display.StencilSize; public int StencilSize => _display.StencilSize;
public IDisposable MakeCurrent() => Context.MakeCurrent(); public IDisposable MakeCurrent() => Context.MakeCurrent();
public IDisposable EnsureCurrent() => MakeCurrent();
public bool IsSharedWith(IGlContext context)
{
var c = (GlContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
public void Dispose() public void Dispose()
{ {
@ -108,7 +123,7 @@ namespace Avalonia.Native
public IGlPlatformSurfaceRenderingSession BeginDraw() public IGlPlatformSurfaceRenderingSession BeginDraw()
{ {
var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var feature = (AvaloniaNativePlatformOpenGlInterface)AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing()); return new GlPlatformSurfaceRenderingSession(_context, _target.BeginDrawing());
} }

6
src/Avalonia.Native/PopupImpl.cs

@ -9,12 +9,12 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts; private readonly AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature; private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
private readonly IWindowBaseImpl _parent; private readonly IWindowBaseImpl _parent;
public PopupImpl(IAvaloniaNativeFactory factory, public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature, AvaloniaNativePlatformOpenGlInterface glFeature,
IWindowBaseImpl parent) : base(opts, glFeature) IWindowBaseImpl parent) : base(opts, glFeature)
{ {
_factory = factory; _factory = factory;
@ -23,7 +23,7 @@ namespace Avalonia.Native
_parent = parent; _parent = parent;
using (var e = new PopupEvents(this)) using (var e = new PopupEvents(this))
{ {
var context = _opts.UseGpu ? glFeature?.DeferredContext : null; var context = _opts.UseGpu ? glFeature?.MainContext : null;
Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context); Init(factory.CreatePopup(e, context?.Context), factory.CreateScreens(), context);
} }
PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize)); PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(parent, MoveResize));

6
src/Avalonia.Native/WindowImpl.cs

@ -14,19 +14,19 @@ namespace Avalonia.Native
{ {
private readonly IAvaloniaNativeFactory _factory; private readonly IAvaloniaNativeFactory _factory;
private readonly AvaloniaNativePlatformOptions _opts; private readonly AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature; private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
IAvnWindow _native; IAvnWindow _native;
private double _extendTitleBarHeight = -1; private double _extendTitleBarHeight = -1;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature) : base(opts, glFeature) AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
{ {
_factory = factory; _factory = factory;
_opts = opts; _opts = opts;
_glFeature = glFeature; _glFeature = glFeature;
using (var e = new WindowEvents(this)) using (var e = new WindowEvents(this))
{ {
var context = _opts.UseGpu ? glFeature?.DeferredContext : null; var context = _opts.UseGpu ? glFeature?.MainContext : null;
Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context); Init(_native = factory.CreateWindow(e, context?.Context), factory.CreateScreens(), context);
} }

2
src/Avalonia.Native/WindowImplBase.cs

@ -61,7 +61,7 @@ namespace Avalonia.Native
private NativeControlHostImpl _nativeControlHost; private NativeControlHostImpl _nativeControlHost;
private IGlContext _glContext; private IGlContext _glContext;
internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature) internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, AvaloniaNativePlatformOpenGlInterface glFeature)
{ {
_gpu = opts.UseGpu && glFeature != null; _gpu = opts.UseGpu && glFeature != null;
_deferredRendering = opts.UseDeferredRendering; _deferredRendering = opts.UseDeferredRendering;

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

@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
namespace Avalonia.OpenGL.Angle namespace Avalonia.OpenGL.Angle
{ {

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

@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
using static Avalonia.OpenGL.EglConsts; using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL.Angle namespace Avalonia.OpenGL.Angle
{ {
@ -52,7 +52,7 @@ namespace Avalonia.OpenGL.Angle
} }
} }
private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, info.Display) private AngleWin32EglDisplay(EglInterface egl, AngleInfo info) : base(egl, false, info.Display)
{ {
PlatformApi = info.PlatformApi; PlatformApi = info.PlatformApi;
} }
@ -78,18 +78,18 @@ namespace Avalonia.OpenGL.Angle
return d3dDeviceHandle; return d3dDeviceHandle;
} }
public EglSurface WrapDirect3D11Texture(IntPtr handle) public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle)
{ {
if (PlatformApi != AngleOptions.PlatformApi.DirectX11) if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi); throw new InvalidOperationException("Current platform API is " + PlatformApi);
return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE, EGL_TRUE, EGL_NONE }); return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
} }
public EglSurface WrapDirect3D11Texture(IntPtr handle, int offsetX, int offsetY, int width, int height) public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle, int offsetX, int offsetY, int width, int height)
{ {
if (PlatformApi != AngleOptions.PlatformApi.DirectX11) if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi); throw new InvalidOperationException("Current platform API is " + PlatformApi);
return 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 }); 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 });
} }
} }
} }

192
src/Avalonia.OpenGL/OpenGlControlBase.cs → src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs

@ -3,44 +3,83 @@ using Avalonia.Controls;
using Avalonia.Logging; using Avalonia.Logging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.OpenGL.Imaging; using Avalonia.OpenGL.Imaging;
using Avalonia.Rendering;
using Avalonia.VisualTree;
using static Avalonia.OpenGL.GlConsts; using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Controls
{ {
public abstract class OpenGlControlBase : Control public abstract class OpenGlControlBase : Control
{ {
private IGlContext _context; private IGlContext _context;
private int _fb, _texture, _renderBuffer; private int _fb, _depthBuffer;
private OpenGlTextureBitmap _bitmap; private OpenGlBitmap _bitmap;
private PixelSize _oldSize; private IOpenGlBitmapAttachment _attachment;
private PixelSize _depthBufferSize;
private bool _glFailed; private bool _glFailed;
private bool _initialized;
protected GlVersion GlVersion { get; private set; } protected GlVersion GlVersion { get; private set; }
public sealed override void Render(DrawingContext context) public sealed override void Render(DrawingContext context)
{ {
if(!EnsureInitialized()) if(!EnsureInitialized())
return; return;
using (_context.MakeCurrent()) using (_context.MakeCurrent())
{ {
using (_bitmap.Lock()) _context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
{ EnsureTextureAttachment();
var gl = _context.GlInterface; EnsureDepthBufferAttachment(_context.GlInterface);
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); if(!CheckFramebufferStatus(_context.GlInterface))
if (_oldSize != GetPixelSize()) return;
ResizeTexture(gl);
OnOpenGlRender(_context.GlInterface, _fb);
OnOpenGlRender(gl, _fb); _attachment.Present();
gl.Flush();
}
} }
context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds); context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds);
base.Render(context); base.Render(context);
} }
private void CheckError(GlInterface gl)
{
int err;
while ((err = gl.GetError()) != GL_NO_ERROR)
Console.WriteLine(err);
}
void EnsureTextureAttachment()
{
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
if (_bitmap == null || _attachment == null || _bitmap.PixelSize != GetPixelSize())
{
_attachment?.Dispose();
_attachment = null;
_bitmap?.Dispose();
_bitmap = null;
_bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
_attachment = _bitmap.CreateFramebufferAttachment(_context);
}
}
void EnsureDepthBufferAttachment(GlInterface gl)
{
var size = GetPixelSize();
if (size == _depthBufferSize && _depthBuffer != 0)
return;
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
if (_depthBuffer != 0) gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
var oneArr = new int[1];
gl.GenRenderbuffers(1, oneArr);
_depthBuffer = oneArr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
gl.RenderbufferStorage(GL_RENDERBUFFER,
GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
size.Width, size.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
}
void DoCleanup(bool callUserDeinit) void DoCleanup()
{ {
if (_context != null) if (_context != null)
{ {
@ -50,16 +89,19 @@ namespace Avalonia.OpenGL
gl.BindTexture(GL_TEXTURE_2D, 0); gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb }); gl.DeleteFramebuffers(1, new[] { _fb });
using (_bitmap.Lock()) gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
_bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1); _attachment?.Dispose();
gl.DeleteTextures(1, new[] { _texture }); _attachment = null;
gl.DeleteRenderbuffers(1, new[] { _renderBuffer }); _bitmap?.Dispose();
_bitmap.Dispose(); _bitmap = null;
try try
{ {
if (callUserDeinit) if (_initialized)
{
_initialized = false;
OnOpenGlDeinit(_context.GlInterface, _fb); OnOpenGlDeinit(_context.GlInterface, _fb);
}
} }
finally finally
{ {
@ -72,11 +114,11 @@ namespace Avalonia.OpenGL
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
DoCleanup(true); DoCleanup();
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);
} }
bool EnsureInitialized() private bool EnsureInitializedCore()
{ {
if (_context != null) if (_context != null)
return true; return true;
@ -84,34 +126,43 @@ namespace Avalonia.OpenGL
if (_glFailed) if (_glFailed)
return false; return false;
var feature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
if (feature == null) if (feature == null)
return false; return false;
if (!feature.CanShareContexts)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: current platform does not support multithreaded context sharing");
return false;
}
try try
{ {
_context = feature.CreateContext(); _context = feature.CreateSharedContext();
} }
catch (Exception e) catch (Exception e)
{ {
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e); "Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e);
_glFailed = true;
return false; return false;
} }
GlVersion = _context.Version; GlVersion = _context.Version;
try try
{ {
_bitmap = new OpenGlTextureBitmap(); _bitmap = new OpenGlBitmap(GetPixelSize(), new Vector(96, 96));
if (!_bitmap.SupportsContext(_context))
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlBitmap: OpenGL context is not compatible");
return false;
}
} }
catch (Exception e) catch (Exception e)
{ {
_context.Dispose(); _context.Dispose();
_context = null; _context = null;
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e); "Unable to initialize OpenGL: unable to create OpenGlBitmap: {exception}", e);
_glFailed = true;
return false; return false;
} }
@ -119,80 +170,55 @@ namespace Avalonia.OpenGL
{ {
try try
{ {
_oldSize = GetPixelSize(); _depthBufferSize = GetPixelSize();
var gl = _context.GlInterface; var gl = _context.GlInterface;
var oneArr = new int[1]; var oneArr = new int[1];
gl.GenFramebuffers(1, oneArr); gl.GenFramebuffers(1, oneArr);
_fb = oneArr[0]; _fb = oneArr[0];
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb); gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
gl.GenTextures(1, oneArr);
_texture = oneArr[0];
ResizeTexture(gl); EnsureDepthBufferAttachment(gl);
EnsureTextureAttachment();
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER); return CheckFramebufferStatus(gl);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
int code;
while ((code = gl.GetError()) != 0)
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {code}", code);
_glFailed = true;
return false;
}
} }
catch(Exception e) catch(Exception e)
{ {
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase", Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {exception}", e); "Unable to initialize OpenGL FBO: {exception}", e);
_glFailed = true; return false;
} }
if (!_glFailed)
OnOpenGlInit(_context.GlInterface, _fb);
} }
}
if (_glFailed) private bool CheckFramebufferStatus(GlInterface gl)
{
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{ {
DoCleanup(false); int code;
while ((code = gl.GetError()) != 0)
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL FBO: {code}", code);
return false;
} }
return true; return true;
} }
void ResizeTexture(GlInterface gl) private bool EnsureInitialized()
{ {
var size = GetPixelSize(); if (_initialized)
return true;
gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture); _glFailed = !(_initialized = EnsureInitializedCore());
gl.BindTexture(GL_TEXTURE_2D, _texture); if (_glFailed)
gl.TexImage2D(GL_TEXTURE_2D, 0, return false;
GlVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8, using (_context.MakeCurrent())
size.Width, size.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero); OnOpenGlInit(_context.GlInterface, _fb);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); return true;
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
var oneArr = new int[1];
gl.GenRenderbuffers(1, oneArr);
_renderBuffer = oneArr[0];
gl.BindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
gl.RenderbufferStorage(GL_RENDERBUFFER,
GlVersion.Type == GlProfileType.OpenGLES ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT,
size.Width, size.Height);
gl.FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderBuffer);
gl.BindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
using (_bitmap.Lock())
_bitmap.SetTexture(_texture, GL_RGBA8, size, 1);
} }
PixelSize GetPixelSize() private PixelSize GetPixelSize()
{ {
var scaling = VisualRoot.RenderScaling; var scaling = VisualRoot.RenderScaling;
return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)), return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)),

2
src/Avalonia.OpenGL/EglConsts.cs → src/Avalonia.OpenGL/Egl/EglConsts.cs

@ -1,6 +1,6 @@
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
// ReSharper disable IdentifierTypo // ReSharper disable IdentifierTypo
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public static class EglConsts public static class EglConsts
{ {

69
src/Avalonia.OpenGL/EglContext.cs → src/Avalonia.OpenGL/Egl/EglContext.cs

@ -1,23 +1,25 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Threading; using System.Threading;
using static Avalonia.OpenGL.EglConsts; using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglContext : IGlContext public class EglContext : IGlContext
{ {
private readonly EglDisplay _disp; private readonly EglDisplay _disp;
private readonly EglInterface _egl; private readonly EglInterface _egl;
private readonly EglContext _sharedWith;
private readonly object _lock = new object(); private readonly object _lock = new object();
public EglContext(EglDisplay display, EglInterface egl, IntPtr ctx, EglSurface offscreenSurface, public EglContext(EglDisplay display, EglInterface egl, EglContext sharedWith, IntPtr ctx, Func<EglContext, EglSurface> offscreenSurface,
GlVersion version, int sampleCount, int stencilSize) GlVersion version, int sampleCount, int stencilSize)
{ {
_disp = display; _disp = display;
_egl = egl; _egl = egl;
_sharedWith = sharedWith;
Context = ctx; Context = ctx;
OffscreenSurface = offscreenSurface; OffscreenSurface = offscreenSurface(this);
Version = version; Version = version;
SampleCount = sampleCount; SampleCount = sampleCount;
StencilSize = stencilSize; StencilSize = stencilSize;
@ -33,21 +35,17 @@ namespace Avalonia.OpenGL
public int StencilSize { get; } public int StencilSize { get; }
public EglDisplay Display => _disp; public EglDisplay Display => _disp;
public IDisposable Lock()
{
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
class RestoreContext : IDisposable class RestoreContext : IDisposable
{ {
private readonly EglInterface _egl; private readonly EglInterface _egl;
private readonly object _l;
private readonly IntPtr _display; private readonly IntPtr _display;
private IntPtr _context, _read, _draw; private IntPtr _context, _read, _draw;
public RestoreContext(EglInterface egl, IntPtr defDisplay) public RestoreContext(EglInterface egl, IntPtr defDisplay, object l)
{ {
_egl = egl; _egl = egl;
_l = l;
_display = _egl.GetCurrentDisplay(); _display = _egl.GetCurrentDisplay();
if (_display == IntPtr.Zero) if (_display == IntPtr.Zero)
_display = defDisplay; _display = defDisplay;
@ -59,29 +57,52 @@ namespace Avalonia.OpenGL
public void Dispose() public void Dispose()
{ {
_egl.MakeCurrent(_display, _draw, _read, _context); _egl.MakeCurrent(_display, _draw, _read, _context);
Monitor.Exit(_l);
} }
} }
public IDisposable MakeCurrent() public IDisposable MakeCurrent() => MakeCurrent(OffscreenSurface);
public IDisposable MakeCurrent(EglSurface surface)
{ {
var old = new RestoreContext(_egl, _disp.Handle); Monitor.Enter(_lock);
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); var success = false;
if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context)) try
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl); {
return old; var old = new RestoreContext(_egl, _disp.Handle, _lock);
var surf = surface ?? OffscreenSurface;
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
success = true;
return old;
}
finally
{
if(!success)
Monitor.Enter(_lock);
}
} }
public IDisposable MakeCurrent(EglSurface surface) public IDisposable EnsureCurrent()
{ {
var old = new RestoreContext(_egl, _disp.Handle); if(IsCurrent)
var surf = surface ?? OffscreenSurface; return Disposable.Empty;
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); return MakeCurrent();
if (!_egl.MakeCurrent(_disp.Handle, surf.DangerousGetHandle(), surf.DangerousGetHandle(), Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
return old;
} }
public bool IsSharedWith(IGlContext context)
{
var c = (EglContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
public bool IsCurrent => _egl.GetCurrentDisplay() == _disp.Handle && _egl.GetCurrentContext() == Context;
public void Dispose() public void Dispose()
{ {
_egl.DestroyContext(_disp.Handle, Context); _egl.DestroyContext(_disp.Handle, Context);

53
src/Avalonia.OpenGL/EglDisplay.cs → src/Avalonia.OpenGL/Egl/EglDisplay.cs

@ -1,26 +1,25 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using static Avalonia.OpenGL.Egl.EglConsts;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglDisplay public class EglDisplay
{ {
private readonly EglInterface _egl; private readonly EglInterface _egl;
public bool SupportsSharing { get; }
private readonly IntPtr _display; private readonly IntPtr _display;
private readonly IntPtr _config; private readonly IntPtr _config;
private readonly int[] _contextAttributes; private readonly int[] _contextAttributes;
private readonly int _surfaceType; private readonly int _surfaceType;
public IntPtr Handle => _display; public IntPtr Handle => _display;
public IntPtr Config => _config;
private int _sampleCount; private int _sampleCount;
private int _stencilSize; private int _stencilSize;
private GlVersion _version; private GlVersion _version;
public EglDisplay(EglInterface egl) : this(egl, -1, IntPtr.Zero, null) public EglDisplay(EglInterface egl, bool supportsSharing) : this(egl, supportsSharing, -1, IntPtr.Zero, null)
{ {
} }
@ -45,15 +44,16 @@ namespace Avalonia.OpenGL
return display; return display;
} }
public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs) public EglDisplay(EglInterface egl, bool supportsSharing, int platformType, IntPtr platformDisplay, int[] attrs)
: this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs)) : this(egl, supportsSharing, CreateDisplay(egl, platformType, platformDisplay, attrs))
{ {
} }
public EglDisplay(EglInterface egl, IntPtr display) public EglDisplay(EglInterface egl, bool supportsSharing, IntPtr display)
{ {
_egl = egl; _egl = egl;
SupportsSharing = supportsSharing;
_display = display; _display = display;
if(_display == IntPtr.Zero) if(_display == IntPtr.Zero)
throw new ArgumentException(); throw new ArgumentException();
@ -136,7 +136,12 @@ namespace Avalonia.OpenGL
throw new OpenGlException("No suitable EGL config was found"); throw new OpenGlException("No suitable EGL config was found");
} }
public EglDisplay() : this(new EglInterface()) public EglDisplay() : this(false)
{
}
public EglDisplay(bool supportsSharing) : this(new EglInterface(), supportsSharing)
{ {
} }
@ -144,6 +149,9 @@ namespace Avalonia.OpenGL
public EglInterface EglInterface => _egl; public EglInterface EglInterface => _egl;
public EglContext CreateContext(IGlContext share) public EglContext CreateContext(IGlContext share)
{ {
if (share != null && !SupportsSharing)
throw new NotSupportedException("Context sharing is not supported by this display");
if((_surfaceType|EGL_PBUFFER_BIT) == 0) if((_surfaceType|EGL_PBUFFER_BIT) == 0)
throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces"); throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces");
var shareCtx = (EglContext)share; var shareCtx = (EglContext)share;
@ -158,37 +166,22 @@ namespace Avalonia.OpenGL
}); });
if (surf == IntPtr.Zero) if (surf == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl); throw OpenGlException.GetFormattedException("eglCreatePBufferSurface", _egl);
var rv = new EglContext(this, _egl, ctx, new EglSurface(this, _egl, surf), var rv = new EglContext(this, _egl, shareCtx, ctx, context => new EglSurface(this, context, surf),
_version, _sampleCount, _stencilSize); _version, _sampleCount, _stencilSize);
return rv; return rv;
} }
public EglContext CreateContext(EglContext share, EglSurface offscreenSurface) public EglContext CreateContext(EglContext share, EglSurface offscreenSurface)
{ {
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); var ctx = _egl.CreateContext(_display, _config, share?.Context ?? IntPtr.Zero, _contextAttributes);
if (ctx == IntPtr.Zero) if (ctx == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateContext", _egl); throw OpenGlException.GetFormattedException("eglCreateContext", _egl);
var rv = new EglContext(this, _egl, ctx, offscreenSurface, _version, _sampleCount, _stencilSize); var rv = new EglContext(this, _egl, share, ctx, _ => offscreenSurface, _version, _sampleCount, _stencilSize);
rv.MakeCurrent(null); rv.MakeCurrent(null);
return rv; return rv;
} }
public EglSurface CreateWindowSurface(IntPtr window)
{
var s = _egl.CreateWindowSurface(_display, _config, window, new[] {EGL_NONE, EGL_NONE});
if (s == IntPtr.Zero)
throw OpenGlException.GetFormattedException("eglCreateWindowSurface", _egl);
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);
}
} }
} }

2
src/Avalonia.OpenGL/EglErrors.cs → src/Avalonia.OpenGL/Egl/EglErrors.cs

@ -1,4 +1,4 @@
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public enum EglErrors public enum EglErrors
{ {

54
src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs

@ -0,0 +1,54 @@
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.OpenGL.Egl
{
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{
private readonly EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_egl = egl;
_info = info;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _egl.CreateWindowSurface(_info.Handle);
return new RenderTarget(_egl, glSurface, _info);
}
class RenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglPlatformOpenGlInterface _egl;
private EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private PixelSize _currentSize;
public RenderTarget(EglPlatformOpenGlInterface egl,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(egl)
{
_egl = egl;
_glSurface = glSurface;
_info = info;
_currentSize = info.Size;
}
public override void Dispose() => _glSurface.Dispose();
public override IGlPlatformSurfaceRenderingSession BeginDraw()
{
if (_info.Size != _currentSize || _glSurface == null)
{
_glSurface?.Dispose();
_glSurface = null;
_glSurface = _egl.CreateWindowSurface(_info.Handle);
_currentSize = _info.Size;
}
return base.BeginDraw(_glSurface, _info);
}
}
}
}

47
src/Avalonia.OpenGL/EglGlPlatformSurfaceBase.cs → src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs

@ -1,6 +1,7 @@
using System; using System;
using Avalonia.OpenGL.Surfaces;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
{ {
@ -14,19 +15,15 @@ namespace Avalonia.OpenGL
public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget(); public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
} }
public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTargetWithCorruptionInfo public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTarget
{ {
private readonly EglDisplay _display; private readonly EglPlatformOpenGlInterface _egl;
private readonly EglContext _context;
protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context) protected EglPlatformSurfaceRenderTargetBase(EglPlatformOpenGlInterface egl)
{ {
_display = display; _egl = egl;
_context = context;
} }
public abstract bool IsCorrupted { get; }
public virtual void Dispose() public virtual void Dispose()
{ {
@ -37,22 +34,25 @@ namespace Avalonia.OpenGL
protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface, protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false) EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
{ {
var l = _context.Lock();
var restoreContext = _egl.PrimaryEglContext.MakeCurrent(surface);
var success = false;
try try
{ {
if (IsCorrupted) var egli = _egl.Display.EglInterface;
throw new RenderTargetCorruptedException(); egli.WaitClient();
var restoreContext = _context.MakeCurrent(surface); egli.WaitGL();
_display.EglInterface.WaitClient(); egli.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); _egl.PrimaryContext.GlInterface.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, 0);
return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped); success = true;
return new Session(_egl.Display, _egl.PrimaryEglContext, surface, info, restoreContext, onFinish, isYFlipped);
} }
catch finally
{ {
l.Dispose(); if(!success)
throw; restoreContext.Dispose();
} }
} }
@ -62,21 +62,19 @@ namespace Avalonia.OpenGL
private readonly EglSurface _glSurface; private readonly EglSurface _glSurface;
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly IDisposable _lock;
private readonly IDisposable _restoreContext; private readonly IDisposable _restoreContext;
private readonly Action _onFinish; private readonly Action _onFinish;
public Session(EglDisplay display, EglContext context, public Session(EglDisplay display, EglContext context,
EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped) IDisposable restoreContext, Action onFinish, bool isYFlipped)
{ {
IsYFlipped = isYFlipped; IsYFlipped = isYFlipped;
_context = context; _context = context;
_display = display; _display = display;
_glSurface = glSurface; _glSurface = glSurface;
_info = info; _info = info;
_lock = @lock;
_restoreContext = restoreContext; _restoreContext = restoreContext;
_onFinish = onFinish; _onFinish = onFinish;
} }
@ -90,7 +88,6 @@ namespace Avalonia.OpenGL
_display.EglInterface.WaitGL(); _display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE); _display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_restoreContext.Dispose(); _restoreContext.Dispose();
_lock.Dispose();
_onFinish?.Invoke(); _onFinish?.Invoke();
} }

2
src/Avalonia.OpenGL/EglInterface.cs → src/Avalonia.OpenGL/Egl/EglInterface.cs

@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglInterface : GlInterfaceBase public class EglInterface : GlInterfaceBase
{ {

72
src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs

@ -0,0 +1,72 @@
using System;
using Avalonia.Logging;
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;
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)
{
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);
}
}
}
}

11
src/Avalonia.OpenGL/EglSurface.cs → src/Avalonia.OpenGL/Egl/EglSurface.cs

@ -1,22 +1,25 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Egl
{ {
public class EglSurface : SafeHandle public class EglSurface : SafeHandle
{ {
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglInterface _egl; private readonly EglInterface _egl;
public EglSurface(EglDisplay display, EglInterface egl, IntPtr surface) : base(surface, true) public EglSurface(EglDisplay display, EglContext context, IntPtr surface) : base(surface, true)
{ {
_display = display; _display = display;
_egl = egl; _context = context;
_egl = display.EglInterface;
} }
protected override bool ReleaseHandle() protected override bool ReleaseHandle()
{ {
_egl.DestroySurface(_display.Handle, handle); using (_context.MakeCurrent())
_egl.DestroySurface(_display.Handle, handle);
return true; return true;
} }

43
src/Avalonia.OpenGL/EglGlPlatformFeature.cs

@ -1,43 +0,0 @@
using System;
using Avalonia.Logging;
namespace Avalonia.OpenGL
{
public class EglGlPlatformFeature : IWindowingPlatformGlFeature
{
private EglDisplay _display;
public EglDisplay Display => _display;
public IGlContext CreateContext()
{
return _display.CreateContext(DeferredContext);
}
public EglContext DeferredContext { get; private set; }
public IGlContext MainContext => DeferredContext;
public static void TryInitialize()
{
var feature = TryCreate();
if (feature != null)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature);
}
public static EglGlPlatformFeature TryCreate() => TryCreate(() => new EglDisplay());
public static EglGlPlatformFeature TryCreate(Func<EglDisplay> displayFactory)
{
try
{
var disp = displayFactory();
return new EglGlPlatformFeature
{
_display = disp,
DeferredContext = disp.CreateContext(null)
};
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log(null, "Unable to initialize EGL-based rendering: {0}", e);
return null;
}
}
}
}

51
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@ -1,51 +0,0 @@
using System;
using System.Threading;
namespace Avalonia.OpenGL
{
public class EglGlPlatformSurface : EglGlPlatformSurfaceBase
{
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
public EglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base()
{
_display = context.Display;
_context = context;
_info = info;
}
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
var glSurface = _display.CreateWindowSurface(_info.Handle);
return new RenderTarget(_display, _context, glSurface, _info);
}
class RenderTarget : EglPlatformSurfaceRenderTargetBase
{
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglSurface _glSurface;
private readonly IEglWindowGlPlatformSurfaceInfo _info;
private PixelSize _initialSize;
public RenderTarget(EglDisplay display, EglContext context,
EglSurface glSurface, IEglWindowGlPlatformSurfaceInfo info) : base(display, context)
{
_display = display;
_context = context;
_glSurface = glSurface;
_info = info;
_initialSize = info.Size;
}
public override void Dispose() => _glSurface.Dispose();
public override bool IsCorrupted => _initialSize != _info.Size;
public override IGlPlatformSurfaceRenderingSession BeginDraw() => base.BeginDraw(_glSurface, _info);
}
}
}

13
src/Avalonia.OpenGL/GlInterface.cs

@ -82,6 +82,9 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glFlush")] [GlEntryPoint("glFlush")]
public Action Flush { get; } public Action Flush { get; }
[GlEntryPoint("glFinish")]
public Action Finish { get; }
public delegate IntPtr GlGetString(int v); public delegate IntPtr GlGetString(int v);
[GlEntryPoint("glGetString")] [GlEntryPoint("glGetString")]
@ -144,6 +147,10 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glBindTexture")] [GlEntryPoint("glBindTexture")]
public GlBindTexture BindTexture { get; } public GlBindTexture BindTexture { get; }
public delegate void GlActiveTexture(int texture);
[GlEntryPoint("glActiveTexture")]
public GlActiveTexture ActiveTexture { get; }
public delegate void GlDeleteTextures(int count, int[] textures); public delegate void GlDeleteTextures(int count, int[] textures);
[GlEntryPoint("glDeleteTextures")] [GlEntryPoint("glDeleteTextures")]
public GlDeleteTextures DeleteTextures { get; } public GlDeleteTextures DeleteTextures { get; }
@ -154,6 +161,12 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glTexImage2D")] [GlEntryPoint("glTexImage2D")]
public GlTexImage2D TexImage2D { get; } public GlTexImage2D TexImage2D { get; }
public delegate void GlCopyTexSubImage2D(int target, int level, int xoffset, int yoffset, int x, int y,
int width, int height);
[GlEntryPoint("glCopyTexSubImage2D")]
public GlCopyTexSubImage2D CopyTexSubImage2D { get; }
public delegate void GlTexParameteri(int target, int name, int value); public delegate void GlTexParameteri(int target, int name, int value);
[GlEntryPoint("glTexParameteri")] [GlEntryPoint("glTexParameteri")]
public GlTexParameteri TexParameteri { get; } public GlTexParameteri TexParameteri { get; }

2
src/Avalonia.OpenGL/IGlContext.cs

@ -9,5 +9,7 @@ namespace Avalonia.OpenGL
int SampleCount { get; } int SampleCount { get; }
int StencilSize { get; } int StencilSize { get; }
IDisposable MakeCurrent(); IDisposable MakeCurrent();
IDisposable EnsureCurrent();
bool IsSharedWith(IGlContext context);
} }
} }

2
src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs

@ -4,6 +4,6 @@ namespace Avalonia.OpenGL
{ {
public interface IOpenGlAwarePlatformRenderInterface public interface IOpenGlAwarePlatformRenderInterface
{ {
IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi);
} }
} }

13
src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs

@ -0,0 +1,13 @@
namespace Avalonia.OpenGL
{
public interface IPlatformOpenGlInterface
{
IGlContext PrimaryContext { get; }
IGlContext CreateSharedContext();
bool CanShareContexts { get; }
bool CanCreateContexts { get; }
IGlContext CreateContext();
/*IGlContext TryCreateContext(GlVersion version);
*/
}
}

8
src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs

@ -1,8 +0,0 @@
namespace Avalonia.OpenGL
{
public interface IWindowingPlatformGlFeature
{
IGlContext CreateContext();
IGlContext MainContext { get; }
}
}

17
src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs

@ -0,0 +1,17 @@
using System;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Avalonia.OpenGL.Imaging
{
public interface IOpenGlBitmapImpl : IBitmapImpl
{
IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback);
bool SupportsContext(IGlContext context);
}
public interface IOpenGlBitmapAttachment : IDisposable
{
void Present();
}
}

13
src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs

@ -1,13 +0,0 @@
using System;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace Avalonia.OpenGL.Imaging
{
public interface IOpenGlTextureBitmapImpl : IBitmapImpl
{
IDisposable Lock();
void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling);
void SetDirty();
}
}

34
src/Avalonia.OpenGL/Imaging/OpenGlTextureBitmap.cs → src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs

@ -6,32 +6,30 @@ using Avalonia.Threading;
namespace Avalonia.OpenGL.Imaging namespace Avalonia.OpenGL.Imaging
{ {
public class OpenGlTextureBitmap : Bitmap, IAffectsRender public class OpenGlBitmap : Bitmap, IAffectsRender
{ {
private IOpenGlTextureBitmapImpl _impl; private IOpenGlBitmapImpl _impl;
static IOpenGlTextureBitmapImpl CreateOrThrow()
public OpenGlBitmap(PixelSize size, Vector dpi)
: base(CreateOrThrow(size, dpi))
{ {
if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface _impl = (IOpenGlBitmapImpl)PlatformImpl.Item;
glAware))
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
return glAware.CreateOpenGlTextureBitmap();
} }
public OpenGlTextureBitmap() static IOpenGlBitmapImpl CreateOrThrow(PixelSize size, Vector dpi)
: base(CreateOrThrow())
{ {
_impl = (IOpenGlTextureBitmapImpl)PlatformImpl.Item; if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface
glAware))
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
return glAware.CreateOpenGlBitmap(size, dpi);
} }
public IDisposable Lock() => _impl.Lock(); public IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context) =>
_impl.CreateFramebufferAttachment(context, SetIsDirty);
public void SetTexture(int textureId, int internalFormat, PixelSize size, double dpiScaling) public bool SupportsContext(IGlContext context) => _impl.SupportsContext(context);
{
_impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling); void SetIsDirty()
SetIsDirty();
}
public void SetIsDirty()
{ {
if (Dispatcher.UIThread.CheckAccess()) if (Dispatcher.UIThread.CheckAccess())
CallInvalidated(); CallInvalidated();

1
src/Avalonia.OpenGL/OpenGlException.cs

@ -1,4 +1,5 @@
using System; using System;
using Avalonia.OpenGL.Egl;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurface.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs

@ -1,4 +1,4 @@
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurface public interface IGlPlatformSurface
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderTarget.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs

@ -1,6 +1,6 @@
using System; using System;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurfaceRenderTarget : IDisposable public interface IGlPlatformSurfaceRenderTarget : IDisposable
{ {

2
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs → src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs

@ -1,6 +1,6 @@
using System; using System;
namespace Avalonia.OpenGL namespace Avalonia.OpenGL.Surfaces
{ {
public interface IGlPlatformSurfaceRenderingSession : IDisposable public interface IGlPlatformSurfaceRenderingSession : IDisposable
{ {

53
src/Avalonia.X11/Glx/GlxContext.cs

@ -8,18 +8,21 @@ namespace Avalonia.X11.Glx
{ {
public IntPtr Handle { get; } public IntPtr Handle { get; }
public GlxInterface Glx { get; } public GlxInterface Glx { get; }
private readonly GlxContext _sharedWith;
private readonly X11Info _x11; private readonly X11Info _x11;
private readonly IntPtr _defaultXid; private readonly IntPtr _defaultXid;
private readonly bool _ownsPBuffer; private readonly bool _ownsPBuffer;
private readonly object _lock = new object(); private readonly object _lock = new object();
public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display, public GlxContext(GlxInterface glx, IntPtr handle, GlxDisplay display,
GlxContext sharedWith,
GlVersion version, int sampleCount, int stencilSize, GlVersion version, int sampleCount, int stencilSize,
X11Info x11, IntPtr defaultXid, X11Info x11, IntPtr defaultXid,
bool ownsPBuffer) bool ownsPBuffer)
{ {
Handle = handle; Handle = handle;
Glx = glx; Glx = glx;
_sharedWith = sharedWith;
_x11 = x11; _x11 = x11;
_defaultXid = defaultXid; _defaultXid = defaultXid;
_ownsPBuffer = ownsPBuffer; _ownsPBuffer = ownsPBuffer;
@ -37,25 +40,21 @@ namespace Avalonia.X11.Glx
public int SampleCount { get; } public int SampleCount { get; }
public int StencilSize { get; } public int StencilSize { get; }
public IDisposable Lock()
{
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
class RestoreContext : IDisposable class RestoreContext : IDisposable
{ {
private GlxInterface _glx; private GlxInterface _glx;
private IntPtr _defaultDisplay; private IntPtr _defaultDisplay;
private readonly object _l;
private IntPtr _display; private IntPtr _display;
private IntPtr _context; private IntPtr _context;
private IntPtr _read; private IntPtr _read;
private IntPtr _draw; private IntPtr _draw;
public RestoreContext(GlxInterface glx, IntPtr defaultDisplay) public RestoreContext(GlxInterface glx, IntPtr defaultDisplay, object l)
{ {
_glx = glx; _glx = glx;
_defaultDisplay = defaultDisplay; _defaultDisplay = defaultDisplay;
_l = l;
_display = _glx.GetCurrentDisplay(); _display = _glx.GetCurrentDisplay();
_context = _glx.GetCurrentContext(); _context = _glx.GetCurrentContext();
_read = _glx.GetCurrentReadDrawable(); _read = _glx.GetCurrentReadDrawable();
@ -66,19 +65,49 @@ namespace Avalonia.X11.Glx
{ {
var disp = _display == IntPtr.Zero ? _defaultDisplay : _display; var disp = _display == IntPtr.Zero ? _defaultDisplay : _display;
_glx.MakeContextCurrent(disp, _draw, _read, _context); _glx.MakeContextCurrent(disp, _draw, _read, _context);
Monitor.Exit(_l);
} }
} }
public IDisposable MakeCurrent() => MakeCurrent(_defaultXid); public IDisposable MakeCurrent() => MakeCurrent(_defaultXid);
public IDisposable EnsureCurrent()
{
if(IsCurrent)
return Disposable.Empty;
return MakeCurrent();
}
public bool IsSharedWith(IGlContext context)
{
var c = (GlxContext)context;
return c == this
|| c._sharedWith == this
|| _sharedWith == context
|| _sharedWith != null && _sharedWith == c._sharedWith;
}
public IDisposable MakeCurrent(IntPtr xid) public IDisposable MakeCurrent(IntPtr xid)
{ {
var old = new RestoreContext(Glx, _x11.Display); Monitor.Enter(_lock);
if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle)) var success = false;
throw new OpenGlException("glXMakeContextCurrent failed "); try
return old; {
var old = new RestoreContext(Glx, _x11.Display, _lock);
if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
throw new OpenGlException("glXMakeContextCurrent failed ");
success = true;
return old;
}
finally
{
if (!success)
Monitor.Exit(_lock);
}
} }
public bool IsCurrent => Glx.GetCurrentContext() == Handle;
public void Dispose() public void Dispose()
{ {
Glx.DestroyContext(_x11.Display, Handle); Glx.DestroyContext(_x11.Display, Handle);

8
src/Avalonia.X11/Glx/GlxDisplay.cs

@ -113,9 +113,9 @@ namespace Avalonia.X11.Glx
} }
public GlxContext CreateContext() => CreateContext(DeferredContext); public GlxContext CreateContext() => CreateContext();
GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share, public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true); share.SampleCount, share.StencilSize, true);
GlxContext CreateContext(IntPtr defaultXid, IGlContext share, GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
@ -144,7 +144,7 @@ namespace Avalonia.X11.Glx
if (handle != IntPtr.Zero) if (handle != IntPtr.Zero)
{ {
_version = profile; _version = profile;
return new GlxContext(new GlxInterface(), handle, this, profile, return new GlxContext(new GlxInterface(), handle, this, (GlxContext)share, profile,
sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer); sampleCount, stencilSize, _x11, defaultXid, ownsPBuffer);
} }

25
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@ -1,5 +1,8 @@
using System; using System;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
@ -40,33 +43,26 @@ namespace Avalonia.X11.Glx
public IGlPlatformSurfaceRenderingSession BeginDraw() public IGlPlatformSurfaceRenderingSession BeginDraw()
{ {
var l = _context.Lock(); var oldContext = _context.MakeCurrent(_info.Handle);
try
{ // Reset to default FBO first
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, 0);
return new Session(_context, _info, l, _context.MakeCurrent(_info.Handle));
} return new Session(_context, _info, oldContext);
catch
{
l.Dispose();
throw;
}
} }
class Session : IGlPlatformSurfaceRenderingSession class Session : IGlPlatformSurfaceRenderingSession
{ {
private readonly GlxContext _context; private readonly GlxContext _context;
private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info; private readonly EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo _info;
private IDisposable _lock;
private readonly IDisposable _clearContext; private readonly IDisposable _clearContext;
public IGlContext Context => _context; public IGlContext Context => _context;
public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info, public Session(GlxContext context, EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable clearContext) IDisposable clearContext)
{ {
_context = context; _context = context;
_info = info; _info = info;
_lock = @lock;
_clearContext = clearContext; _clearContext = clearContext;
} }
@ -77,7 +73,6 @@ namespace Avalonia.X11.Glx
_context.Display.SwapBuffers(_info.Handle); _context.Display.SwapBuffers(_info.Handle);
_context.Glx.WaitX(); _context.Glx.WaitX();
_clearContext.Dispose(); _clearContext.Dispose();
_lock.Dispose();
} }
public PixelSize Size => _info.Size; public PixelSize Size => _info.Size;

13
src/Avalonia.X11/Glx/GlxPlatformFeature.cs

@ -5,31 +5,34 @@ using Avalonia.OpenGL;
namespace Avalonia.X11.Glx namespace Avalonia.X11.Glx
{ {
class GlxGlPlatformFeature : IWindowingPlatformGlFeature class GlxPlatformOpenGlInterface : IPlatformOpenGlInterface
{ {
public GlxDisplay Display { get; private set; } public GlxDisplay Display { get; private set; }
public bool CanCreateContexts => true;
public bool CanShareContexts => true;
public IGlContext CreateContext() => Display.CreateContext(); public IGlContext CreateContext() => Display.CreateContext();
public IGlContext CreateSharedContext() => Display.CreateContext(PrimaryContext);
public GlxContext DeferredContext { get; private set; } public GlxContext DeferredContext { get; private set; }
public IGlContext MainContext => DeferredContext; public IGlContext PrimaryContext => DeferredContext;
public static bool TryInitialize(X11Info x11, IList<GlVersion> glProfiles) public static bool TryInitialize(X11Info x11, IList<GlVersion> glProfiles)
{ {
var feature = TryCreate(x11, glProfiles); var feature = TryCreate(x11, glProfiles);
if (feature != null) if (feature != null)
{ {
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(feature); AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(feature);
return true; return true;
} }
return false; return false;
} }
public static GlxGlPlatformFeature TryCreate(X11Info x11, IList<GlVersion> glProfiles) public static GlxPlatformOpenGlInterface TryCreate(X11Info x11, IList<GlVersion> glProfiles)
{ {
try try
{ {
var disp = new GlxDisplay(x11, glProfiles); var disp = new GlxDisplay(x11, glProfiles);
return new GlxGlPlatformFeature return new GlxPlatformOpenGlInterface
{ {
Display = disp, Display = disp,
DeferredContext = disp.DeferredContext DeferredContext = disp.DeferredContext

5
src/Avalonia.X11/X11Platform.cs

@ -7,6 +7,7 @@ using Avalonia.FreeDesktop;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Platform; using Avalonia.Input.Platform;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.X11; using Avalonia.X11;
@ -70,9 +71,9 @@ namespace Avalonia.X11
if (options.UseGpu) if (options.UseGpu)
{ {
if (options.UseEGL) if (options.UseEGL)
EglGlPlatformFeature.TryInitialize(); EglPlatformOpenGlInterface.TryInitialize();
else else
GlxGlPlatformFeature.TryInitialize(Info, Options.GlProfiles); GlxPlatformOpenGlInterface.TryInitialize(Info, Options.GlProfiles);
} }

9
src/Avalonia.X11/X11Window.cs

@ -12,6 +12,7 @@ using Avalonia.FreeDesktop;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Threading; using Avalonia.Threading;
@ -65,7 +66,7 @@ namespace Avalonia.X11
_touch = new TouchDevice(); _touch = new TouchDevice();
_keyboard = platform.KeyboardDevice; _keyboard = platform.KeyboardDevice;
var glfeature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var glfeature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
XSetWindowAttributes attr = new XSetWindowAttributes(); XSetWindowAttributes attr = new XSetWindowAttributes();
var valueMask = default(SetWindowValuemask); var valueMask = default(SetWindowValuemask);
@ -87,13 +88,13 @@ namespace Avalonia.X11
// OpenGL seems to be do weird things to it's current window which breaks resize sometimes // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
_useRenderWindow = glfeature != null; _useRenderWindow = glfeature != null;
var glx = glfeature as GlxGlPlatformFeature; var glx = glfeature as GlxPlatformOpenGlInterface;
if (glx != null) if (glx != null)
visualInfo = *glx.Display.VisualInfo; visualInfo = *glx.Display.VisualInfo;
else if (glfeature == null) else if (glfeature == null)
visualInfo = _x11.TransparentVisualInfo; visualInfo = _x11.TransparentVisualInfo;
var egl = glfeature as EglGlPlatformFeature; var egl = glfeature as EglPlatformOpenGlInterface;
var visual = IntPtr.Zero; var visual = IntPtr.Zero;
var depth = 24; var depth = 24;
@ -168,7 +169,7 @@ namespace Avalonia.X11
if (egl != null) if (egl != null)
surfaces.Insert(0, surfaces.Insert(0,
new EglGlPlatformSurface(egl.DeferredContext, new EglGlPlatformSurface(egl,
new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle))); new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
if (glx != null) if (glx != null)
surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext, surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,

4
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -32,8 +32,8 @@ namespace Avalonia.LinuxFramebuffer
void Initialize() void Initialize()
{ {
Threading = new InternalPlatformThreadingInterface(); Threading = new InternalPlatformThreadingInterface();
if (_fb is IWindowingPlatformGlFeature glFeature) if (_fb is IGlOutputBackend gl)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(glFeature); AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(gl.PlatformOpenGlInterface);
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>().ToConstant(Threading) .Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60)) .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))

18
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs

@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform.Interop; using Avalonia.Platform.Interop;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods; using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
using static Avalonia.LinuxFramebuffer.Output.LibDrm; using static Avalonia.LinuxFramebuffer.Output.LibDrm;
@ -11,13 +13,16 @@ using static Avalonia.LinuxFramebuffer.Output.LibDrm.GbmColorFormats;
namespace Avalonia.LinuxFramebuffer.Output namespace Avalonia.LinuxFramebuffer.Output
{ {
public unsafe class DrmOutput : IOutputBackend, IGlPlatformSurface, IWindowingPlatformGlFeature public unsafe class DrmOutput : IGlOutputBackend, IGlPlatformSurface
{ {
private DrmCard _card; private DrmCard _card;
private readonly EglGlPlatformSurface _eglPlatformSurface; private readonly EglGlPlatformSurface _eglPlatformSurface;
public PixelSize PixelSize => _mode.Resolution; public PixelSize PixelSize => _mode.Resolution;
public double Scaling { get; set; } public double Scaling { get; set; }
public IGlContext MainContext => _deferredContext; public IGlContext PrimaryContext => _deferredContext;
private EglPlatformOpenGlInterface _platformGl;
public IPlatformOpenGlInterface PlatformOpenGlInterface => _platformGl;
public DrmOutput(string path = null) public DrmOutput(string path = null)
{ {
@ -132,10 +137,9 @@ namespace Avalonia.LinuxFramebuffer.Output
if(_gbmTargetSurface == null) if(_gbmTargetSurface == null)
throw new InvalidOperationException("Unable to create GBM surface"); throw new InvalidOperationException("Unable to create GBM surface");
_eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), false, 0x31D7, device, null);
_platformGl = new EglPlatformOpenGlInterface(_eglDisplay);
_eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), 0x31D7, device, null); _eglSurface = _platformGl.CreateWindowSurface(_gbmTargetSurface);
_eglSurface = _eglDisplay.CreateWindowSurface(_gbmTargetSurface);
EglContext CreateContext(EglContext share) EglContext CreateContext(EglContext share)
@ -144,7 +148,7 @@ namespace Avalonia.LinuxFramebuffer.Output
GbmBoFlags.GBM_BO_USE_RENDERING); GbmBoFlags.GBM_BO_USE_RENDERING);
if (offSurf == null) if (offSurf == null)
throw new InvalidOperationException("Unable to create 1x1 sized GBM surface"); throw new InvalidOperationException("Unable to create 1x1 sized GBM surface");
return _eglDisplay.CreateContext(share, _eglDisplay.CreateWindowSurface(offSurf)); return _eglDisplay.CreateContext(share, _platformGl.CreateWindowSurface(offSurf));
} }
_deferredContext = CreateContext(null); _deferredContext = CreateContext(null);

9
src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs

@ -0,0 +1,9 @@
using Avalonia.OpenGL;
namespace Avalonia.LinuxFramebuffer.Output
{
public interface IGlOutputBackend : IOutputBackend
{
public IPlatformOpenGlInterface PlatformOpenGlInterface { get; }
}
}

2
src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs

@ -19,6 +19,6 @@ namespace Avalonia.Skia
public interface IOpenGlAwareSkiaGpu : ISkiaGpu public interface IOpenGlAwareSkiaGpu : ISkiaGpu
{ {
IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap(); IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi);
} }
} }

1
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using SkiaSharp; using SkiaSharp;

9
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging; using Avalonia.OpenGL.Imaging;
using Avalonia.OpenGL.Surfaces;
using SkiaSharp; using SkiaSharp;
namespace Avalonia.Skia namespace Avalonia.Skia
@ -8,10 +9,12 @@ namespace Avalonia.Skia
class GlSkiaGpu : IOpenGlAwareSkiaGpu class GlSkiaGpu : IOpenGlAwareSkiaGpu
{ {
private GRContext _grContext; private GRContext _grContext;
private IGlContext _glContext;
public GlSkiaGpu(IWindowingPlatformGlFeature gl, long? maxResourceBytes) public GlSkiaGpu(IPlatformOpenGlInterface openGl, long? maxResourceBytes)
{ {
var context = gl.MainContext; var context = openGl.PrimaryContext;
_glContext = context;
using (context.MakeCurrent()) using (context.MakeCurrent())
{ {
using (var iface = context.Version.Type == GlProfileType.OpenGL ? using (var iface = context.Version.Type == GlProfileType.OpenGL ?
@ -40,6 +43,6 @@ namespace Avalonia.Skia
return null; return null;
} }
public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() => new OpenGlTextureBitmapImpl(); public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi) => new GlOpenGlBitmapImpl(_glContext, size, dpi);
} }
} }

207
src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.Utilities;
using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia
{
class GlOpenGlBitmapImpl : IOpenGlBitmapImpl, IDrawableBitmapImpl
{
private readonly IGlContext _context;
private readonly object _lock = new object();
private IGlPresentableOpenGlSurface _surface;
public GlOpenGlBitmapImpl(IGlContext context, PixelSize pixelSize, Vector dpi)
{
_context = context;
PixelSize = pixelSize;
Dpi = dpi;
}
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public int Version { get; private set; }
public void Save(string fileName) => throw new NotSupportedException();
public void Save(Stream stream) => throw new NotSupportedException();
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{
lock (_lock)
{
if (_surface == null)
return;
using (_surface.Lock())
{
using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false,
new GRGlTextureInfo(
GlConsts.GL_TEXTURE_2D, (uint)_surface.GetTextureId(),
(uint)_surface.InternalFormat)))
using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft,
SKColorType.Rgba8888))
{
// Again, silently ignore, if something went wrong it's not our fault
if (surface == null)
return;
using (var snapshot = surface.Snapshot())
context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint);
}
}
}
}
public IOpenGlBitmapAttachment CreateFramebufferAttachment(IGlContext context, Action presentCallback)
{
if (!SupportsContext(context))
throw new OpenGlException("Context is not supported for texture sharing");
return new SharedOpenGlBitmapAttachment(this, context, presentCallback);
}
public bool SupportsContext(IGlContext context)
{
// TODO: negotiated platform surface sharing
return _context.IsSharedWith(context);
}
public void Dispose()
{
}
internal void Present(IGlPresentableOpenGlSurface surface)
{
lock (_lock)
{
_surface = surface;
}
}
}
interface IGlPresentableOpenGlSurface : IDisposable
{
int GetTextureId();
int InternalFormat { get; }
IDisposable Lock();
}
class SharedOpenGlBitmapAttachment : IOpenGlBitmapAttachment, IGlPresentableOpenGlSurface
{
private readonly GlOpenGlBitmapImpl _bitmap;
private readonly IGlContext _context;
private readonly Action _presentCallback;
private readonly int _fbo;
private readonly int _texture;
private readonly int _frontBuffer;
private bool _disposed;
private readonly DisposableLock _lock = new DisposableLock();
public SharedOpenGlBitmapAttachment(GlOpenGlBitmapImpl bitmap, IGlContext context, Action presentCallback)
{
_bitmap = bitmap;
_context = context;
_presentCallback = presentCallback;
using (_context.EnsureCurrent())
{
var glVersion = _context.Version;
InternalFormat = glVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8;
_context.GlInterface.GetIntegerv(GL_FRAMEBUFFER_BINDING, out _fbo);
if (_fbo == 0)
throw new OpenGlException("Current FBO is 0");
{
var gl = _context.GlInterface;
var textures = new int[2];
gl.GenTextures(2, textures);
_texture = textures[0];
_frontBuffer = textures[1];
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
foreach (var t in textures)
{
gl.BindTexture(GL_TEXTURE_2D, t);
gl.TexImage2D(GL_TEXTURE_2D, 0,
InternalFormat,
_bitmap.PixelSize.Width, _bitmap.PixelSize.Height,
0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
}
}
}
public void Present()
{
using (_context.MakeCurrent())
{
if (_disposed)
throw new ObjectDisposedException(nameof(SharedOpenGlBitmapAttachment));
var gl = _context.GlInterface;
gl.Finish();
using (Lock())
{
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
gl.GetIntegerv(GL_TEXTURE_BINDING_2D, out var oldTexture);
gl.GetIntegerv(GL_ACTIVE_TEXTURE, out var oldActive);
gl.BindFramebuffer(GL_FRAMEBUFFER, _fbo);
gl.BindTexture(GL_TEXTURE_2D, _frontBuffer);
gl.ActiveTexture(GL_TEXTURE0);
gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, _bitmap.PixelSize.Width,
_bitmap.PixelSize.Height);
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
gl.BindTexture(GL_TEXTURE_2D, oldTexture);
gl.ActiveTexture(oldActive);
gl.Finish();
}
}
_bitmap.Present(this);
_presentCallback();
}
public void Dispose()
{
var gl = _context.GlInterface;
_bitmap.Present(null);
if(_disposed)
return;
using (_context.MakeCurrent())
using (Lock())
{
if(_disposed)
return;
_disposed = true;
gl.DeleteTextures(2, new[] { _texture, _frontBuffer });
}
}
int IGlPresentableOpenGlSurface.GetTextureId()
{
return _frontBuffer;
}
public int InternalFormat { get; }
public IDisposable Lock() => _lock.Lock();
}
}

81
src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs

@ -1,81 +0,0 @@
using System;
using System.IO;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.Skia.Helpers;
using Avalonia.Utilities;
using SkiaSharp;
namespace Avalonia.Skia
{
class OpenGlTextureBitmapImpl : IOpenGlTextureBitmapImpl, IDrawableBitmapImpl
{
private DisposableLock _lock = new DisposableLock();
private int _textureId;
private int _internalFormat;
public void Dispose()
{
using (Lock())
{
_textureId = 0;
PixelSize = new PixelSize(1, 1);
Version++;
}
}
public Vector Dpi { get; private set; } = new Vector(96, 96);
public PixelSize PixelSize { get; private set; } = new PixelSize(1, 1);
public int Version { get; private set; } = 0;
public void Save(string fileName) => throw new System.NotSupportedException();
public void Save(Stream stream) => throw new System.NotSupportedException();
public void Draw(DrawingContextImpl context, SKRect sourceRect, SKRect destRect, SKPaint paint)
{
// For now silently ignore
if (context.GrContext == null)
return;
using (Lock())
{
if (_textureId == 0)
return;
using (var backendTexture = new GRBackendTexture(PixelSize.Width, PixelSize.Height, false,
new GRGlTextureInfo(
GlConsts.GL_TEXTURE_2D, (uint)_textureId,
(uint)_internalFormat)))
using (var surface = SKSurface.Create(context.GrContext, backendTexture, GRSurfaceOrigin.TopLeft,
SKColorType.Rgba8888))
{
// Again, silently ignore, if something went wrong it's not our fault
if (surface == null)
return;
using (var snapshot = surface.Snapshot())
context.Canvas.DrawImage(snapshot, sourceRect, destRect, paint);
}
}
}
public IDisposable Lock() => _lock.Lock();
public void SetBackBuffer(int textureId, int internalFormat, PixelSize pixelSize, double dpiScaling)
{
using (_lock.Lock())
{
_textureId = textureId;
_internalFormat = internalFormat;
PixelSize = pixelSize;
Dpi = new Vector(96 * dpiScaling, 96 * dpiScaling);
Version++;
}
}
public void SetDirty()
{
using (_lock.Lock())
Version++;
}
}
}

6
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@ -30,7 +30,7 @@ namespace Avalonia.Skia
return; return;
} }
var gl = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>(); var gl = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
if (gl != null) if (gl != null)
_skiaGpu = new GlSkiaGpu(gl, maxResourceBytes); _skiaGpu = new GlSkiaGpu(gl, maxResourceBytes);
} }
@ -256,10 +256,10 @@ namespace Avalonia.Skia
} }
public IOpenGlTextureBitmapImpl CreateOpenGlTextureBitmap() public IOpenGlBitmapImpl CreateOpenGlBitmap(PixelSize size, Vector dpi)
{ {
if (_skiaGpu is IOpenGlAwareSkiaGpu glAware) if (_skiaGpu is IOpenGlAwareSkiaGpu glAware)
return glAware.CreateOpenGlTextureBitmap(); return glAware.CreateOpenGlBitmap(size, dpi);
if (_skiaGpu == null) if (_skiaGpu == null)
throw new PlatformNotSupportedException("GPU acceleration is not available"); throw new PlatformNotSupportedException("GPU acceleration is not available");
throw new PlatformNotSupportedException( throw new PlatformNotSupportedException(

29
src/Windows/Avalonia.Win32/Composition/CompositionEglGlPlatformSurface.cs

@ -2,6 +2,8 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Windows.UI.Composition.Interop; using Windows.UI.Composition.Interop;
namespace Avalonia.Win32 namespace Avalonia.Win32
@ -9,15 +11,15 @@ namespace Avalonia.Win32
internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase internal class CompositionEglGlPlatformSurface : EglGlPlatformSurfaceBase
{ {
private readonly EglDisplay _display; private readonly EglDisplay _display;
private readonly EglContext _context; private EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info; private readonly IEglWindowGlPlatformSurfaceInfo _info;
private ICompositionDrawingSurfaceInterop _surfaceInterop; private ICompositionDrawingSurfaceInterop _surfaceInterop;
private Windows.UI.Composition.Visual _surface; private Windows.UI.Composition.Visual _surface;
public CompositionEglGlPlatformSurface(EglContext context, IEglWindowGlPlatformSurfaceInfo info) : base() public CompositionEglGlPlatformSurface(EglPlatformOpenGlInterface egl, IEglWindowGlPlatformSurfaceInfo info) : base()
{ {
_display = context.Display; _display = egl.Display;
_context = context; _egl = egl;
_info = info; _info = info;
} }
@ -30,26 +32,25 @@ namespace Avalonia.Win32
public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget() public override IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{ {
return new CompositionRenderTarget(_display, _context, _surface, _surfaceInterop, _info); return new CompositionRenderTarget(_egl, _surface, _surfaceInterop, _info);
} }
class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase class CompositionRenderTarget : EglPlatformSurfaceRenderTargetBase
{ {
private readonly EglDisplay _display; private readonly EglPlatformOpenGlInterface _egl;
private readonly IEglWindowGlPlatformSurfaceInfo _info; private readonly IEglWindowGlPlatformSurfaceInfo _info;
private readonly PixelSize _initialSize; private readonly PixelSize _initialSize;
private readonly ICompositionDrawingSurfaceInterop _surfaceInterop; private readonly ICompositionDrawingSurfaceInterop _surfaceInterop;
private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c"); private static Guid s_Iid = Guid.Parse("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
private Windows.UI.Composition.Visual _compositionVisual; private Windows.UI.Composition.Visual _compositionVisual;
public CompositionRenderTarget(EglDisplay display, public CompositionRenderTarget(EglPlatformOpenGlInterface egl,
EglContext context,
Windows.UI.Composition.Visual compositionVisual, Windows.UI.Composition.Visual compositionVisual,
ICompositionDrawingSurfaceInterop interopSurface, ICompositionDrawingSurfaceInterop interopSurface,
IEglWindowGlPlatformSurfaceInfo info) IEglWindowGlPlatformSurfaceInfo info)
: base(display, context) : base(egl)
{ {
_display = display; _egl = egl;
_surfaceInterop = interopSurface; _surfaceInterop = interopSurface;
_info = info; _info = info;
_initialSize = info.Size; _initialSize = info.Size;
@ -58,8 +59,6 @@ namespace Avalonia.Win32
_compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height); _compositionVisual.Size = new System.Numerics.Vector2(_info.Size.Width, _info.Size.Height);
} }
public override bool IsCorrupted => _info.Size != _initialSize;
public override IGlPlatformSurfaceRenderingSession BeginDraw() public override IGlPlatformSurfaceRenderingSession BeginDraw()
{ {
var offset = new POINT(); var offset = new POINT();
@ -69,7 +68,7 @@ namespace Avalonia.Win32
ref s_Iid, ref s_Iid,
out IntPtr texture, ref offset); out IntPtr texture, ref offset);
var surface = (_display as AngleWin32EglDisplay).WrapDirect3D11Texture(texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height); var surface = (_egl.Display as AngleWin32EglDisplay).WrapDirect3D11Texture(_egl, texture, offset.X, offset.Y, _info.Size.Width, _info.Size.Height);
return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true); return base.BeginDraw(surface, _info, () => { _surfaceInterop.EndDraw(); Marshal.Release(texture); surface.Dispose(); }, true);
} }

2
src/Windows/Avalonia.Win32/Composition/CompositionHost.cs

@ -56,7 +56,7 @@ namespace Avalonia.Win32
var interop = _compositor.As<ICompositorInterop>(); var interop = _compositor.As<ICompositorInterop>();
var display = Win32GlManager.EglFeature.Display as AngleWin32EglDisplay; var display = Win32GlManager.EglPlatformInterface.Display as AngleWin32EglDisplay;
_graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice()); _graphicsDevice = interop.CreateGraphicsDevice(display.GetDirect3DDevice());
} }

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

@ -1,26 +1,27 @@
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
namespace Avalonia.Win32 namespace Avalonia.Win32
{ {
static class Win32GlManager static class Win32GlManager
{ {
/// <summary>This property is initialized if drawing platform requests OpenGL support</summary> /// <summary>This property is initialized if drawing platform requests OpenGL support</summary>
public static EglGlPlatformFeature EglFeature { get; private set; } public static EglPlatformOpenGlInterface EglPlatformInterface { get; private set; }
private static bool s_attemptedToInitialize; private static bool s_attemptedToInitialize;
public static void Initialize() public static void Initialize()
{ {
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToFunc(() => AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToFunc(() =>
{ {
if (!s_attemptedToInitialize) if (!s_attemptedToInitialize)
{ {
EglFeature = EglGlPlatformFeature.TryCreate(() => new AngleWin32EglDisplay()); EglPlatformInterface = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
s_attemptedToInitialize = true; s_attemptedToInitialize = true;
} }
return EglFeature; return EglPlatformInterface;
}); });
} }
} }

11
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -8,6 +8,8 @@ using Avalonia.Input;
using Avalonia.Input.Raw; using Avalonia.Input.Raw;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle; using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering; using Avalonia.Rendering;
using Avalonia.Win32.Input; using Avalonia.Win32.Input;
@ -103,17 +105,18 @@ namespace Avalonia.Win32
_isUsingComposition = Win32Platform.Options.UseWindowsUIComposition && _isUsingComposition = Win32Platform.Options.UseWindowsUIComposition &&
Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 && Win32Platform.WindowsVersion.Major >= 10 && Win32Platform.WindowsVersion.Build >= 16299 &&
Win32GlManager.EglFeature.Display is AngleWin32EglDisplay angleDisplay && Win32GlManager.EglPlatformInterface != null &&
Win32GlManager.EglPlatformInterface.Display is AngleWin32EglDisplay angleDisplay &&
angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11; angleDisplay.PlatformApi == AngleOptions.PlatformApi.DirectX11;
CreateWindow(); CreateWindow();
_framebuffer = new FramebufferManager(_hwnd); _framebuffer = new FramebufferManager(_hwnd);
if (Win32GlManager.EglFeature != null) if (Win32GlManager.EglPlatformInterface != null)
{ {
if (_isUsingComposition) if (_isUsingComposition)
{ {
var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); var cgl = new CompositionEglGlPlatformSurface(Win32GlManager.EglPlatformInterface, this);
_blurHost = cgl.AttachToCompositionTree(_hwnd); _blurHost = cgl.AttachToCompositionTree(_hwnd);
_gl = cgl; _gl = cgl;
@ -122,7 +125,7 @@ namespace Avalonia.Win32
} }
else else
{ {
_gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this); _gl = new EglGlPlatformSurface(Win32GlManager.EglPlatformInterface, this);
} }
} }

20
src/iOS/Avalonia.iOS/EaglDisplay.cs

@ -1,15 +1,18 @@
using System; using System;
using System.Reactive.Disposables;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using OpenGLES; using OpenGLES;
using OpenTK.Graphics.ES30; using OpenTK.Graphics.ES30;
namespace Avalonia.iOS namespace Avalonia.iOS
{ {
class EaglFeature : IWindowingPlatformGlFeature class EaglFeature : IPlatformOpenGlInterface
{ {
public IGlContext PrimaryContext => Context;
public IGlContext CreateSharedContext() => throw new NotSupportedException();
public bool CanShareContexts => false;
public bool CanCreateContexts => false;
public IGlContext CreateContext() => throw new System.NotSupportedException(); public IGlContext CreateContext() => throw new System.NotSupportedException();
public IGlContext MainContext => Context;
public GlContext Context { get; } = new GlContext(); public GlContext Context { get; } = new GlContext();
} }
@ -61,9 +64,18 @@ namespace Avalonia.iOS
return new ResetContext(old); return new ResetContext(old);
} }
public IDisposable EnsureCurrent()
{
if(EAGLContext.CurrentContext == Context)
return Disposable.Empty;
return MakeCurrent();
}
public bool IsSharedWith(IGlContext context) => false;
public GlVersion Version { get; } = new GlVersion(GlProfileType.OpenGLES, 3, 0); public GlVersion Version { get; } = new GlVersion(GlProfileType.OpenGLES, 3, 0);
public GlInterface GlInterface { get; } public GlInterface GlInterface { get; }
public int SampleCount { get; } = 0; public int SampleCount { get; } = 0;
public int StencilSize { get; } = 9; public int StencilSize { get; } = 9;
} }
} }

3
src/iOS/Avalonia.iOS/EaglLayerSurface.cs

@ -2,6 +2,7 @@
using System; using System;
using System.Threading; using System.Threading;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.OpenGL.Surfaces;
using CoreAnimation; using CoreAnimation;
using OpenTK.Graphics.ES30; using OpenTK.Graphics.ES30;
@ -91,4 +92,4 @@ namespace Avalonia.iOS
} }
} }
} }
} }

2
src/iOS/Avalonia.iOS/Platform.cs

@ -26,7 +26,7 @@ namespace Avalonia.iOS
var keyboard = new KeyboardDevice(); var keyboard = new KeyboardDevice();
var softKeyboard = new SoftKeyboardHelper(); var softKeyboard = new SoftKeyboardHelper();
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatformGlFeature>().ToConstant(GlFeature) .Bind<IPlatformOpenGlInterface>().ToConstant(GlFeature)
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactoryStub()) .Bind<IStandardCursorFactory>().ToConstant(new CursorFactoryStub())
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub()) .Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
.Bind<IClipboard>().ToConstant(new ClipboardImpl()) .Bind<IClipboard>().ToConstant(new ClipboardImpl())

4
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Direct.cs

@ -94,8 +94,8 @@ namespace Avalonia.Base.UnitTests
Class1.FooProperty.Changed.Subscribe(e => Class1.FooProperty.Changed.Subscribe(e =>
raised = e.Property == Class1.FooProperty && raised = e.Property == Class1.FooProperty &&
(string)e.OldValue == "initial" && e.OldValue.GetValueOrDefault() == "initial" &&
(string)e.NewValue == "newvalue" && e.NewValue.GetValueOrDefault() == "newvalue" &&
e.Priority == BindingPriority.LocalValue); e.Priority == BindingPriority.LocalValue);
target.SetValue(Class1.FooProperty, "newvalue"); target.SetValue(Class1.FooProperty, "newvalue");

4
tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs

@ -83,7 +83,7 @@ namespace Avalonia.Base.UnitTests
var target = new Class1(); var target = new Class1();
string value = null; string value = null;
Class1.FooProperty.Changed.Subscribe(x => value = (string)x.NewValue); Class1.FooProperty.Changed.Subscribe(x => value = x.NewValue.GetValueOrDefault());
target.SetValue(Class1.FooProperty, "newvalue"); target.SetValue(Class1.FooProperty, "newvalue");
Assert.Equal("newvalue", value); Assert.Equal("newvalue", value);
@ -95,7 +95,7 @@ namespace Avalonia.Base.UnitTests
var target = new Class1(); var target = new Class1();
var result = new List<string>(); var result = new List<string>();
Class1.FooProperty.Changed.Subscribe(x => result.Add((string)x.NewValue)); Class1.FooProperty.Changed.Subscribe(x => result.Add(x.NewValue.GetValueOrDefault()));
target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation); target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation);
target.SetValue(Class1.FooProperty, "local"); target.SetValue(Class1.FooProperty, "local");

62
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@ -395,6 +395,53 @@ namespace Avalonia.Controls.UnitTests.Primitives
} }
} }
[Fact]
public void Focusable_Controls_In_Popup_Should_Get_Focus()
{
using (CreateServicesWithFocus())
{
var window = PreparedWindow();
var tb = new TextBox();
var b = new Button();
var p = new Popup
{
PlacementTarget = window,
Child = new StackPanel
{
Children =
{
tb,
b
}
}
};
((ISetLogicalParent)p).SetParent(p.PlacementTarget);
window.Show();
p.Open();
if(p.Host is OverlayPopupHost host)
{
//Need to measure/arrange for visual children to show up
//in OverlayPopupHost
host.Measure(Size.Infinity);
host.Arrange(new Rect(host.DesiredSize));
}
tb.Focus();
Assert.True(FocusManager.Instance?.Current == tb);
//Ensure focus remains in the popup
var nextFocus = KeyboardNavigationHandler.GetNext(FocusManager.Instance.Current, NavigationDirection.Next);
Assert.True(nextFocus == b);
p.Close();
}
}
private IDisposable CreateServices() private IDisposable CreateServices()
{ {
return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform: return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform:
@ -407,6 +454,21 @@ namespace Avalonia.Controls.UnitTests.Primitives
}))); })));
} }
private IDisposable CreateServicesWithFocus()
{
return UnitTestApplication.Start(TestServices.StyledWindow.With(windowingPlatform:
new MockWindowingPlatform(null,
x =>
{
if (UsePopupHost)
return null;
return MockWindowingPlatform.CreatePopupMock(x).Object;
}),
focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice()));
}
private PointerPressedEventArgs CreatePointerPressedEventArgs(Window source, Point p) private PointerPressedEventArgs CreatePointerPressedEventArgs(Window source, Point p)
{ {
var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true); var pointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);

Loading…
Cancel
Save