Browse Source

Refactorings for opengl context and surface management

pull/4741/head
Nikita Tsukanov 5 years ago
parent
commit
b9985a8fa0
  1. 1
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  2. 8
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  3. 41
      src/Avalonia.Native/AvaloniaNativePlatformOpenGlInterface.cs
  4. 6
      src/Avalonia.Native/PopupImpl.cs
  5. 6
      src/Avalonia.Native/WindowImpl.cs
  6. 2
      src/Avalonia.Native/WindowImplBase.cs
  7. 1
      src/Avalonia.OpenGL/Angle/AngleEglInterface.cs
  8. 10
      src/Avalonia.OpenGL/Angle/AngleWin32EglDisplay.cs
  9. 192
      src/Avalonia.OpenGL/Controls/OpenGlControlBase.cs
  10. 2
      src/Avalonia.OpenGL/Egl/EglConsts.cs
  11. 69
      src/Avalonia.OpenGL/Egl/EglContext.cs
  12. 53
      src/Avalonia.OpenGL/Egl/EglDisplay.cs
  13. 2
      src/Avalonia.OpenGL/Egl/EglErrors.cs
  14. 54
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurface.cs
  15. 47
      src/Avalonia.OpenGL/Egl/EglGlPlatformSurfaceBase.cs
  16. 2
      src/Avalonia.OpenGL/Egl/EglInterface.cs
  17. 72
      src/Avalonia.OpenGL/Egl/EglPlatformOpenGlInterface.cs
  18. 11
      src/Avalonia.OpenGL/Egl/EglSurface.cs
  19. 43
      src/Avalonia.OpenGL/EglGlPlatformFeature.cs
  20. 51
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  21. 13
      src/Avalonia.OpenGL/GlInterface.cs
  22. 2
      src/Avalonia.OpenGL/IGlContext.cs
  23. 2
      src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs
  24. 13
      src/Avalonia.OpenGL/IPlatformOpenGlInterface.cs
  25. 8
      src/Avalonia.OpenGL/IWindowingPlatformGlFeature.cs
  26. 17
      src/Avalonia.OpenGL/Imaging/IOpenGlBitmapImpl.cs
  27. 13
      src/Avalonia.OpenGL/Imaging/IOpenGlTextureBitmapImpl.cs
  28. 34
      src/Avalonia.OpenGL/Imaging/OpenGlBitmap.cs
  29. 1
      src/Avalonia.OpenGL/OpenGlException.cs
  30. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurface.cs
  31. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderTarget.cs
  32. 2
      src/Avalonia.OpenGL/Surfaces/IGlPlatformSurfaceRenderingSession.cs
  33. 53
      src/Avalonia.X11/Glx/GlxContext.cs
  34. 8
      src/Avalonia.X11/Glx/GlxDisplay.cs
  35. 25
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  36. 13
      src/Avalonia.X11/Glx/GlxPlatformFeature.cs
  37. 5
      src/Avalonia.X11/X11Platform.cs
  38. 9
      src/Avalonia.X11/X11Window.cs
  39. 4
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  40. 18
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  41. 9
      src/Linux/Avalonia.LinuxFramebuffer/Output/IGlOutputBackend.cs
  42. 2
      src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs
  43. 1
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlRenderTarget.cs
  44. 9
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs
  45. 207
      src/Skia/Avalonia.Skia/Gpu/OpenGl/OpenGlBitmapImpl.cs
  46. 81
      src/Skia/Avalonia.Skia/Gpu/OpenGlTextureBitmapImpl.cs
  47. 6
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  48. 9
      src/Windows/Avalonia.Win32/Win32GlManager.cs
  49. 6
      src/Windows/Avalonia.Win32/WindowImpl.cs
  50. 20
      src/iOS/Avalonia.iOS/EaglDisplay.cs
  51. 3
      src/iOS/Avalonia.iOS/EaglLayerSurface.cs
  52. 2
      src/iOS/Avalonia.iOS/Platform.cs

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

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

8
src/Avalonia.Native/AvaloniaNativePlatform.cs

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

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

@ -2,21 +2,20 @@
using Avalonia.OpenGL;
using Avalonia.Native.Interop;
using System.Drawing;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Threading;
namespace Avalonia.Native
{
class GlPlatformFeature : IWindowingPlatformGlFeature
class AvaloniaNativePlatformOpenGlInterface : IPlatformOpenGlInterface
{
private readonly IAvnGlDisplay _display;
public GlPlatformFeature(IAvnGlDisplay display)
public AvaloniaNativePlatformOpenGlInterface(IAvnGlDisplay display)
{
_display = display;
var immediate = display.CreateContext(null);
var deferred = display.CreateContext(immediate);
int major, minor;
GlInterface glInterface;
using (immediate.MakeCurrent())
@ -33,19 +32,22 @@ namespace Avalonia.Native
}
GlDisplay = new GlDisplay(display, glInterface, immediate.SampleCount, immediate.StencilSize);
ImmediateContext = new GlContext(GlDisplay, immediate, _version);
DeferredContext = new GlContext(GlDisplay, deferred, _version);
MainContext = new GlContext(GlDisplay, null, immediate, _version);
}
internal IGlContext ImmediateContext { get; }
public IGlContext MainContext => DeferredContext;
internal GlContext DeferredContext { get; }
internal GlContext MainContext { get; }
public IGlContext PrimaryContext => MainContext;
public bool CanShareContexts => true;
public bool CanCreateContexts => true;
internal GlDisplay GlDisplay;
private readonly GlVersion _version;
public IGlContext CreateSharedContext() => new GlContext(GlDisplay,
MainContext, _display.CreateContext(MainContext.Context), _version);
public IGlContext CreateContext() => new GlContext(GlDisplay,
_display.CreateContext(((GlContext)ImmediateContext).Context), _version);
null, _display.CreateContext(null), _version);
}
class GlDisplay
@ -72,11 +74,13 @@ namespace Avalonia.Native
class GlContext : IGlContext
{
private readonly GlDisplay _display;
private readonly GlContext _sharedWith;
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;
_sharedWith = sharedWith;
Context = context;
Version = version;
}
@ -86,6 +90,17 @@ namespace Avalonia.Native
public int SampleCount => _display.SampleCount;
public int StencilSize => _display.StencilSize;
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()
{
@ -108,7 +123,7 @@ namespace Avalonia.Native
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
var feature = (AvaloniaNativePlatformOpenGlInterface)AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
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 AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature;
private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
private readonly IWindowBaseImpl _parent;
public PopupImpl(IAvaloniaNativeFactory factory,
AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature,
AvaloniaNativePlatformOpenGlInterface glFeature,
IWindowBaseImpl parent) : base(opts, glFeature)
{
_factory = factory;
@ -23,7 +23,7 @@ namespace Avalonia.Native
_parent = parent;
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);
}
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 AvaloniaNativePlatformOptions _opts;
private readonly GlPlatformFeature _glFeature;
private readonly AvaloniaNativePlatformOpenGlInterface _glFeature;
IAvnWindow _native;
private double _extendTitleBarHeight = -1;
internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
GlPlatformFeature glFeature) : base(opts, glFeature)
AvaloniaNativePlatformOpenGlInterface glFeature) : base(opts, glFeature)
{
_factory = factory;
_opts = opts;
_glFeature = glFeature;
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);
}

2
src/Avalonia.Native/WindowImplBase.cs

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

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

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Platform.Interop;

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

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using static Avalonia.OpenGL.EglConsts;
using Avalonia.OpenGL.Egl;
using static Avalonia.OpenGL.Egl.EglConsts;
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;
}
@ -78,11 +78,11 @@ namespace Avalonia.OpenGL.Angle
return d3dDeviceHandle;
}
public EglSurface WrapDirect3D11Texture(IntPtr handle)
public EglSurface WrapDirect3D11Texture(EglPlatformOpenGlInterface egl, IntPtr handle)
{
if (PlatformApi != AngleOptions.PlatformApi.DirectX11)
throw new InvalidOperationException("Current platform API is " + PlatformApi);
return CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, EGL_NONE });
return egl.CreatePBufferFromClientBuffer(EGL_D3D_TEXTURE_ANGLE, handle, new[] { EGL_NONE, 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.Media;
using Avalonia.OpenGL.Imaging;
using Avalonia.Rendering;
using Avalonia.VisualTree;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Controls
{
public abstract class OpenGlControlBase : Control
{
private IGlContext _context;
private int _fb, _texture, _renderBuffer;
private OpenGlTextureBitmap _bitmap;
private PixelSize _oldSize;
private int _fb, _depthBuffer;
private OpenGlBitmap _bitmap;
private IOpenGlBitmapAttachment _attachment;
private PixelSize _depthBufferSize;
private bool _glFailed;
private bool _initialized;
protected GlVersion GlVersion { get; private set; }
public sealed override void Render(DrawingContext context)
{
if(!EnsureInitialized())
return;
using (_context.MakeCurrent())
{
using (_bitmap.Lock())
{
var gl = _context.GlInterface;
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
if (_oldSize != GetPixelSize())
ResizeTexture(gl);
OnOpenGlRender(gl, _fb);
gl.Flush();
}
_context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, _fb);
EnsureTextureAttachment();
EnsureDepthBufferAttachment(_context.GlInterface);
if(!CheckFramebufferStatus(_context.GlInterface))
return;
OnOpenGlRender(_context.GlInterface, _fb);
_attachment.Present();
}
context.DrawImage(_bitmap, new Rect(_bitmap.Size), Bounds);
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)
{
@ -50,16 +89,19 @@ namespace Avalonia.OpenGL
gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb });
using (_bitmap.Lock())
_bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1);
gl.DeleteTextures(1, new[] { _texture });
gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
_bitmap.Dispose();
gl.DeleteRenderbuffers(1, new[] { _depthBuffer });
_attachment?.Dispose();
_attachment = null;
_bitmap?.Dispose();
_bitmap = null;
try
{
if (callUserDeinit)
if (_initialized)
{
_initialized = false;
OnOpenGlDeinit(_context.GlInterface, _fb);
}
}
finally
{
@ -72,11 +114,11 @@ namespace Avalonia.OpenGL
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
DoCleanup(true);
DoCleanup();
base.OnDetachedFromVisualTree(e);
}
bool EnsureInitialized()
private bool EnsureInitializedCore()
{
if (_context != null)
return true;
@ -84,34 +126,43 @@ namespace Avalonia.OpenGL
if (_glFailed)
return false;
var feature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
var feature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
if (feature == null)
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
{
_context = feature.CreateContext();
_context = feature.CreateSharedContext();
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e);
_glFailed = true;
return false;
}
GlVersion = _context.Version;
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)
{
_context.Dispose();
_context = null;
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e);
_glFailed = true;
"Unable to initialize OpenGL: unable to create OpenGlBitmap: {exception}", e);
return false;
}
@ -119,80 +170,55 @@ namespace Avalonia.OpenGL
{
try
{
_oldSize = GetPixelSize();
_depthBufferSize = GetPixelSize();
var gl = _context.GlInterface;
var oneArr = new int[1];
gl.GenFramebuffers(1, oneArr);
_fb = oneArr[0];
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
gl.GenTextures(1, oneArr);
_texture = oneArr[0];
ResizeTexture(gl);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
EnsureDepthBufferAttachment(gl);
EnsureTextureAttachment();
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
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;
}
return CheckFramebufferStatus(gl);
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"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;
}
void ResizeTexture(GlInterface gl)
private bool EnsureInitialized()
{
var size = GetPixelSize();
gl.GetIntegerv( GL_TEXTURE_BINDING_2D, out var oldTexture);
gl.BindTexture(GL_TEXTURE_2D, _texture);
gl.TexImage2D(GL_TEXTURE_2D, 0,
GlVersion.Type == GlProfileType.OpenGLES ? GL_RGBA : GL_RGBA8,
size.Width, size.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.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);
if (_initialized)
return true;
_glFailed = !(_initialized = EnsureInitializedCore());
if (_glFailed)
return false;
using (_context.MakeCurrent())
OnOpenGlInit(_context.GlInterface, _fb);
return true;
}
PixelSize GetPixelSize()
private PixelSize GetPixelSize()
{
var scaling = VisualRoot.RenderScaling;
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 IdentifierTypo
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Egl
{
public static class EglConsts
{

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

@ -1,23 +1,25 @@
using System;
using System.Reactive.Disposables;
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
{
private readonly EglDisplay _disp;
private readonly EglInterface _egl;
private readonly EglContext _sharedWith;
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)
{
_disp = display;
_egl = egl;
_sharedWith = sharedWith;
Context = ctx;
OffscreenSurface = offscreenSurface;
OffscreenSurface = offscreenSurface(this);
Version = version;
SampleCount = sampleCount;
StencilSize = stencilSize;
@ -33,21 +35,17 @@ namespace Avalonia.OpenGL
public int StencilSize { get; }
public EglDisplay Display => _disp;
public IDisposable Lock()
{
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
class RestoreContext : IDisposable
{
private readonly EglInterface _egl;
private readonly object _l;
private readonly IntPtr _display;
private IntPtr _context, _read, _draw;
public RestoreContext(EglInterface egl, IntPtr defDisplay)
public RestoreContext(EglInterface egl, IntPtr defDisplay, object l)
{
_egl = egl;
_l = l;
_display = _egl.GetCurrentDisplay();
if (_display == IntPtr.Zero)
_display = defDisplay;
@ -59,29 +57,52 @@ namespace Avalonia.OpenGL
public void Dispose()
{
_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);
_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (!_egl.MakeCurrent(_disp.Handle, IntPtr.Zero, IntPtr.Zero, Context))
throw OpenGlException.GetFormattedException("eglMakeCurrent", _egl);
return old;
Monitor.Enter(_lock);
var success = false;
try
{
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);
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);
return old;
if(IsCurrent)
return Disposable.Empty;
return MakeCurrent();
}
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()
{
_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.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Platform.Interop;
using static Avalonia.OpenGL.EglConsts;
using static Avalonia.OpenGL.Egl.EglConsts;
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Egl
{
public class EglDisplay
{
private readonly EglInterface _egl;
public bool SupportsSharing { get; }
private readonly IntPtr _display;
private readonly IntPtr _config;
private readonly int[] _contextAttributes;
private readonly int _surfaceType;
public IntPtr Handle => _display;
public IntPtr Config => _config;
private int _sampleCount;
private int _stencilSize;
private GlVersion _version;
public EglDisplay(EglInterface egl) : 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;
}
public EglDisplay(EglInterface egl, int platformType, IntPtr platformDisplay, int[] attrs)
: this(egl, CreateDisplay(egl, platformType, platformDisplay, attrs))
public EglDisplay(EglInterface egl, bool supportsSharing, int platformType, IntPtr platformDisplay, int[] 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;
SupportsSharing = supportsSharing;
_display = display;
if(_display == IntPtr.Zero)
throw new ArgumentException();
@ -136,7 +136,12 @@ namespace Avalonia.OpenGL
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 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)
throw new InvalidOperationException("Platform doesn't support PBUFFER surfaces");
var shareCtx = (EglContext)share;
@ -158,37 +166,22 @@ namespace Avalonia.OpenGL
});
if (surf == IntPtr.Zero)
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);
return rv;
}
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);
if (ctx == IntPtr.Zero)
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);
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
{

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 Avalonia.OpenGL.Surfaces;
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Egl
{
public abstract class EglGlPlatformSurfaceBase : IGlPlatformSurface
{
@ -14,19 +15,15 @@ namespace Avalonia.OpenGL
public abstract IGlPlatformSurfaceRenderTarget CreateGlRenderTarget();
}
public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTargetWithCorruptionInfo
public abstract class EglPlatformSurfaceRenderTargetBase : IGlPlatformSurfaceRenderTarget
{
private readonly EglDisplay _display;
private readonly EglContext _context;
private readonly EglPlatformOpenGlInterface _egl;
protected EglPlatformSurfaceRenderTargetBase(EglDisplay display, EglContext context)
protected EglPlatformSurfaceRenderTargetBase(EglPlatformOpenGlInterface egl)
{
_display = display;
_context = context;
_egl = egl;
}
public abstract bool IsCorrupted { get; }
public virtual void Dispose()
{
@ -37,22 +34,25 @@ namespace Avalonia.OpenGL
protected IGlPlatformSurfaceRenderingSession BeginDraw(EglSurface surface,
EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info, Action onFinish = null, bool isYFlipped = false)
{
var l = _context.Lock();
var restoreContext = _egl.PrimaryEglContext.MakeCurrent(surface);
var success = false;
try
{
if (IsCorrupted)
throw new RenderTargetCorruptedException();
var restoreContext = _context.MakeCurrent(surface);
_display.EglInterface.WaitClient();
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
return new Session(_display, _context, surface, info, l, restoreContext, onFinish, isYFlipped);
var egli = _egl.Display.EglInterface;
egli.WaitClient();
egli.WaitGL();
egli.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_egl.PrimaryContext.GlInterface.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, 0);
success = true;
return new Session(_egl.Display, _egl.PrimaryEglContext, surface, info, restoreContext, onFinish, isYFlipped);
}
catch
finally
{
l.Dispose();
throw;
if(!success)
restoreContext.Dispose();
}
}
@ -62,21 +62,19 @@ namespace Avalonia.OpenGL
private readonly EglSurface _glSurface;
private readonly EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo _info;
private readonly EglDisplay _display;
private readonly IDisposable _lock;
private readonly IDisposable _restoreContext;
private readonly Action _onFinish;
public Session(EglDisplay display, EglContext context,
EglSurface glSurface, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo info,
IDisposable @lock, IDisposable restoreContext, Action onFinish, bool isYFlipped)
IDisposable restoreContext, Action onFinish, bool isYFlipped)
{
IsYFlipped = isYFlipped;
_context = context;
_display = display;
_glSurface = glSurface;
_info = info;
_lock = @lock;
_restoreContext = restoreContext;
_onFinish = onFinish;
}
@ -90,7 +88,6 @@ namespace Avalonia.OpenGL
_display.EglInterface.WaitGL();
_display.EglInterface.WaitNative(EglConsts.EGL_CORE_NATIVE_ENGINE);
_restoreContext.Dispose();
_lock.Dispose();
_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.Interop;
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Egl
{
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.Runtime.InteropServices;
namespace Avalonia.OpenGL
namespace Avalonia.OpenGL.Egl
{
public class EglSurface : SafeHandle
{
private readonly EglDisplay _display;
private readonly EglContext _context;
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;
_egl = egl;
_context = context;
_egl = display.EglInterface;
}
protected override bool ReleaseHandle()
{
_egl.DestroySurface(_display.Handle, handle);
using (_context.MakeCurrent())
_egl.DestroySurface(_display.Handle, handle);
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")]
public Action Flush { get; }
[GlEntryPoint("glFinish")]
public Action Finish { get; }
public delegate IntPtr GlGetString(int v);
[GlEntryPoint("glGetString")]
@ -144,6 +147,10 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glBindTexture")]
public GlBindTexture BindTexture { get; }
public delegate void GlActiveTexture(int texture);
[GlEntryPoint("glActiveTexture")]
public GlActiveTexture ActiveTexture { get; }
public delegate void GlDeleteTextures(int count, int[] textures);
[GlEntryPoint("glDeleteTextures")]
public GlDeleteTextures DeleteTextures { get; }
@ -154,6 +161,12 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glTexImage2D")]
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);
[GlEntryPoint("glTexParameteri")]
public GlTexParameteri TexParameteri { get; }

2
src/Avalonia.OpenGL/IGlContext.cs

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

2
src/Avalonia.OpenGL/IOpenGlAwarePlatformRenderInterface.cs

@ -4,6 +4,6 @@ namespace Avalonia.OpenGL
{
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
{
public class OpenGlTextureBitmap : Bitmap, IAffectsRender
public class OpenGlBitmap : Bitmap, IAffectsRender
{
private IOpenGlTextureBitmapImpl _impl;
static IOpenGlTextureBitmapImpl CreateOrThrow()
private IOpenGlBitmapImpl _impl;
public OpenGlBitmap(PixelSize size, Vector dpi)
: base(CreateOrThrow(size, dpi))
{
if (!(AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() is IOpenGlAwarePlatformRenderInterface
glAware))
throw new PlatformNotSupportedException("Rendering platform does not support OpenGL integration");
return glAware.CreateOpenGlTextureBitmap();
_impl = (IOpenGlBitmapImpl)PlatformImpl.Item;
}
public OpenGlTextureBitmap()
: base(CreateOrThrow())
static IOpenGlBitmapImpl CreateOrThrow(PixelSize size, Vector dpi)
{
_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)
{
_impl.SetBackBuffer(textureId, internalFormat, size, dpiScaling);
SetIsDirty();
}
public void SetIsDirty()
public bool SupportsContext(IGlContext context) => _impl.SupportsContext(context);
void SetIsDirty()
{
if (Dispatcher.UIThread.CheckAccess())
CallInvalidated();

1
src/Avalonia.OpenGL/OpenGlException.cs

@ -1,4 +1,5 @@
using System;
using Avalonia.OpenGL.Egl;
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
{

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

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

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

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

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

@ -8,18 +8,21 @@ namespace Avalonia.X11.Glx
{
public IntPtr Handle { get; }
public GlxInterface Glx { get; }
private readonly GlxContext _sharedWith;
private readonly X11Info _x11;
private readonly IntPtr _defaultXid;
private readonly bool _ownsPBuffer;
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,
X11Info x11, IntPtr defaultXid,
bool ownsPBuffer)
{
Handle = handle;
Glx = glx;
_sharedWith = sharedWith;
_x11 = x11;
_defaultXid = defaultXid;
_ownsPBuffer = ownsPBuffer;
@ -37,25 +40,21 @@ namespace Avalonia.X11.Glx
public int SampleCount { get; }
public int StencilSize { get; }
public IDisposable Lock()
{
Monitor.Enter(_lock);
return Disposable.Create(() => Monitor.Exit(_lock));
}
class RestoreContext : IDisposable
{
private GlxInterface _glx;
private IntPtr _defaultDisplay;
private readonly object _l;
private IntPtr _display;
private IntPtr _context;
private IntPtr _read;
private IntPtr _draw;
public RestoreContext(GlxInterface glx, IntPtr defaultDisplay)
public RestoreContext(GlxInterface glx, IntPtr defaultDisplay, object l)
{
_glx = glx;
_defaultDisplay = defaultDisplay;
_l = l;
_display = _glx.GetCurrentDisplay();
_context = _glx.GetCurrentContext();
_read = _glx.GetCurrentReadDrawable();
@ -66,19 +65,49 @@ namespace Avalonia.X11.Glx
{
var disp = _display == IntPtr.Zero ? _defaultDisplay : _display;
_glx.MakeContextCurrent(disp, _draw, _read, _context);
Monitor.Exit(_l);
}
}
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)
{
var old = new RestoreContext(Glx, _x11.Display);
if (!Glx.MakeContextCurrent(_x11.Display, xid, xid, Handle))
throw new OpenGlException("glXMakeContextCurrent failed ");
return old;
Monitor.Enter(_lock);
var success = false;
try
{
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()
{
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);
GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
public GlxContext CreateContext() => CreateContext();
public GlxContext CreateContext(IGlContext share) => CreateContext(CreatePBuffer(), share,
share.SampleCount, share.StencilSize, true);
GlxContext CreateContext(IntPtr defaultXid, IGlContext share,
@ -144,7 +144,7 @@ namespace Avalonia.X11.Glx
if (handle != IntPtr.Zero)
{
_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);
}

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

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

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

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

5
src/Avalonia.X11/X11Platform.cs

@ -7,6 +7,7 @@ using Avalonia.FreeDesktop;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.X11;
@ -70,9 +71,9 @@ namespace Avalonia.X11
if (options.UseGpu)
{
if (options.UseEGL)
EglGlPlatformFeature.TryInitialize();
EglPlatformOpenGlInterface.TryInitialize();
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.Raw;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
@ -65,7 +66,7 @@ namespace Avalonia.X11
_touch = new TouchDevice();
_keyboard = platform.KeyboardDevice;
var glfeature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
var glfeature = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
XSetWindowAttributes attr = new XSetWindowAttributes();
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
_useRenderWindow = glfeature != null;
var glx = glfeature as GlxGlPlatformFeature;
var glx = glfeature as GlxPlatformOpenGlInterface;
if (glx != null)
visualInfo = *glx.Display.VisualInfo;
else if (glfeature == null)
visualInfo = _x11.TransparentVisualInfo;
var egl = glfeature as EglGlPlatformFeature;
var egl = glfeature as EglPlatformOpenGlInterface;
var visual = IntPtr.Zero;
var depth = 24;
@ -168,7 +169,7 @@ namespace Avalonia.X11
if (egl != null)
surfaces.Insert(0,
new EglGlPlatformSurface(egl.DeferredContext,
new EglGlPlatformSurface(egl,
new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
if (glx != null)
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()
{
Threading = new InternalPlatformThreadingInterface();
if (_fb is IWindowingPlatformGlFeature glFeature)
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToConstant(glFeature);
if (_fb is IGlOutputBackend gl)
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(gl.PlatformOpenGlInterface);
AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.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.Runtime.InteropServices;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform.Interop;
using static Avalonia.LinuxFramebuffer.NativeUnsafeMethods;
using static Avalonia.LinuxFramebuffer.Output.LibDrm;
@ -11,13 +13,16 @@ using static Avalonia.LinuxFramebuffer.Output.LibDrm.GbmColorFormats;
namespace Avalonia.LinuxFramebuffer.Output
{
public unsafe class DrmOutput : IOutputBackend, IGlPlatformSurface, IWindowingPlatformGlFeature
public unsafe class DrmOutput : IGlOutputBackend, IGlPlatformSurface
{
private DrmCard _card;
private readonly EglGlPlatformSurface _eglPlatformSurface;
public PixelSize PixelSize => _mode.Resolution;
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)
{
@ -132,10 +137,9 @@ namespace Avalonia.LinuxFramebuffer.Output
if(_gbmTargetSurface == null)
throw new InvalidOperationException("Unable to create GBM surface");
_eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), 0x31D7, device, null);
_eglSurface = _eglDisplay.CreateWindowSurface(_gbmTargetSurface);
_eglDisplay = new EglDisplay(new EglInterface(eglGetProcAddress), false, 0x31D7, device, null);
_platformGl = new EglPlatformOpenGlInterface(_eglDisplay);
_eglSurface = _platformGl.CreateWindowSurface(_gbmTargetSurface);
EglContext CreateContext(EglContext share)
@ -144,7 +148,7 @@ namespace Avalonia.LinuxFramebuffer.Output
GbmBoFlags.GBM_BO_USE_RENDERING);
if (offSurf == null)
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);

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
{
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.Reactive.Disposables;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
using Avalonia.Rendering;
using SkiaSharp;

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

@ -1,6 +1,7 @@
using System.Collections.Generic;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Imaging;
using Avalonia.OpenGL.Surfaces;
using SkiaSharp;
namespace Avalonia.Skia
@ -8,10 +9,12 @@ namespace Avalonia.Skia
class GlSkiaGpu : IOpenGlAwareSkiaGpu
{
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 (var iface = context.Version.Type == GlProfileType.OpenGL ?
@ -40,6 +43,6 @@ namespace Avalonia.Skia
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;
}
var gl = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
var gl = AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>();
if (gl != null)
_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)
return glAware.CreateOpenGlTextureBitmap();
return glAware.CreateOpenGlBitmap(size, dpi);
if (_skiaGpu == null)
throw new PlatformNotSupportedException("GPU acceleration is not available");
throw new PlatformNotSupportedException(

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

@ -1,26 +1,27 @@
using Avalonia.OpenGL;
using Avalonia.OpenGL.Angle;
using Avalonia.OpenGL.Egl;
namespace Avalonia.Win32
{
static class Win32GlManager
{
/// <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;
public static void Initialize()
{
AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>().ToFunc(() =>
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToFunc(() =>
{
if (!s_attemptedToInitialize)
{
EglFeature = EglGlPlatformFeature.TryCreate(() => new AngleWin32EglDisplay());
EglPlatformInterface = EglPlatformOpenGlInterface.TryCreate(() => new AngleWin32EglDisplay());
s_attemptedToInitialize = true;
}
return EglFeature;
return EglPlatformInterface;
});
}
}

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

@ -7,6 +7,8 @@ using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Win32.Input;
@ -103,8 +105,8 @@ namespace Avalonia.Win32
CreateWindow();
_framebuffer = new FramebufferManager(_hwnd);
if (Win32GlManager.EglFeature != null)
_gl = new EglGlPlatformSurface(Win32GlManager.EglFeature.DeferredContext, this);
if (Win32GlManager.EglPlatformInterface != null)
_gl = new EglGlPlatformSurface(Win32GlManager.EglPlatformInterface, this);
Screen = new ScreenImpl();

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

@ -1,15 +1,18 @@
using System;
using System.Reactive.Disposables;
using Avalonia.OpenGL;
using OpenGLES;
using OpenTK.Graphics.ES30;
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 MainContext => Context;
public GlContext Context { get; } = new GlContext();
}
@ -61,9 +64,18 @@ namespace Avalonia.iOS
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 GlInterface GlInterface { get; }
public int SampleCount { get; } = 0;
public int StencilSize { get; } = 9;
}
}
}

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

@ -2,6 +2,7 @@
using System;
using System.Threading;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Surfaces;
using CoreAnimation;
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 softKeyboard = new SoftKeyboardHelper();
AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatformGlFeature>().ToConstant(GlFeature)
.Bind<IPlatformOpenGlInterface>().ToConstant(GlFeature)
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactoryStub())
.Bind<IWindowingPlatform>().ToConstant(new WindowingPlatformStub())
.Bind<IClipboard>().ToConstant(new ClipboardImpl())

Loading…
Cancel
Save