Browse Source

Better error and cleanup handling

pull/3386/head
Nikita Tsukanov 7 years ago
parent
commit
170eb56e04
  1. 4
      src/Avalonia.OpenGL/GlInterface.cs
  2. 129
      src/Avalonia.OpenGL/OpenGlControlBase.cs

4
src/Avalonia.OpenGL/GlInterface.cs

@ -92,6 +92,10 @@ namespace Avalonia.OpenGL
[GlEntryPoint("glGenRenderbuffers")]
public GlGenRenderbuffers GenRenderbuffers { get; }
public delegate void GlDeleteRenderbuffers(int count, int[] renderbuffers);
[GlEntryPoint("glDeleteRenderbuffers")]
public GlDeleteTextures DeleteRenderbuffers { get; }
public delegate void GlBindRenderbuffer(int target, int fb);
[GlEntryPoint("glBindRenderbuffer")]
public GlBindRenderbuffer BindRenderbuffer { get; }

129
src/Avalonia.OpenGL/OpenGlControlBase.cs

@ -1,7 +1,10 @@
using System;
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
@ -11,19 +14,21 @@ namespace Avalonia.OpenGL
private IGlContext _context;
private int _fb, _texture, _renderBuffer;
private OpenGlTextureBitmap _bitmap;
private Size _oldSize;
private PixelSize _oldSize;
private bool _glFailed;
protected GlDisplayType DisplayType { get; private set; }
public sealed override void Render(DrawingContext context)
{
if(!EnsureInitialized())
return;
using (_context.MakeCurrent())
{
using (_bitmap.Lock())
{
var gl = _context.Display.GlInterface;
gl.BindFramebuffer(GL_FRAMEBUFFER, _fb);
if (_oldSize != Bounds.Size)
if (_oldSize != GetPixelSize())
ResizeTexture(gl);
OnOpenGlRender(gl, _fb);
@ -35,28 +40,39 @@ namespace Avalonia.OpenGL
base.Render(context);
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
void DoCleanup(bool callUserDeinit)
{
if (_context != null)
{
using (_context.MakeCurrent())
{
OnOpenGlDeinit(_context.Display.GlInterface, _fb);
var gl = _context.Display.GlInterface;
gl.BindTexture(GL_TEXTURE_2D, 0);
gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.DeleteFramebuffers(1, new[] { _fb });
using (_bitmap.Lock())
{
using (_bitmap.Lock())
_bitmap.SetTexture(0, 0, new PixelSize(1, 1), 1);
gl.DeleteTextures(1, new[] { _texture });
}
gl.DeleteTextures(1, new[] { _texture });
gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
_bitmap.Dispose();
_context.Dispose();
_context = null;
try
{
if (callUserDeinit)
OnOpenGlDeinit(_context.Display.GlInterface, _fb);
}
finally
{
_context.Dispose();
_context = null;
}
}
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
DoCleanup(true);
base.OnDetachedFromVisualTree(e);
}
@ -65,45 +81,84 @@ namespace Avalonia.OpenGL
if (_context != null)
return true;
if (_glFailed)
return false;
var feature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
if (feature == null)
return false;
_context = feature.CreateContext();
try
{
_context = feature.CreateContext();
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase",
"Unable to initialize OpenGL: unable to create additional OpenGL context: {exception}", e);
_glFailed = true;
return false;
}
DisplayType = feature.Display.Type;
try
{
_bitmap = new OpenGlTextureBitmap();
}
catch (PlatformNotSupportedException)
catch (Exception e)
{
_context.Dispose();
_context = null;
Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase",
"Unable to initialize OpenGL: unable to create OpenGlTextureBitmap: {exception}", e);
_glFailed = true;
return false;
}
using (_context.MakeCurrent())
{
_oldSize = Bounds.Size;
var gl = _context.Display.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];
gl.BindTexture(GL_TEXTURE_2D, _texture);
ResizeTexture(gl);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
try
{
//TODO: Cleanup
return false;
_oldSize = GetPixelSize();
var gl = _context.Display.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];
gl.BindTexture(GL_TEXTURE_2D, _texture);
ResizeTexture(gl);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
int code;
while ((code = gl.GetError()) != 0)
Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase",
"Unable to initialize OpenGL FBO: {code}", code);
_glFailed = true;
return false;
}
}
catch(Exception e)
{
Logger.TryGet(LogEventLevel.Error)?.Log("OpenGL", "OpenGlControlBase",
"Unable to initialize OpenGL FBO: {exception}", e);
_glFailed = true;
}
OnOpenGlInit(_context.Display.GlInterface, _fb);
if (!_glFailed)
OnOpenGlInit(_context.Display.GlInterface, _fb);
}
if (_glFailed)
{
DoCleanup(false);
}
return true;
@ -118,7 +173,7 @@ namespace Avalonia.OpenGL
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//TODO: destroy the previous one
gl.DeleteRenderbuffers(1, new[] { _renderBuffer });
var oneArr = new int[1];
gl.GenRenderbuffers(1, oneArr);
_renderBuffer = oneArr[0];
@ -131,10 +186,12 @@ namespace Avalonia.OpenGL
_bitmap.SetTexture(_texture, GL_RGBA8, size, 1);
}
//TODO: dpi
PixelSize GetPixelSize() =>
new PixelSize(Math.Max(1, (int)Bounds.Width),
Math.Max(1, (int)Bounds.Height));
PixelSize GetPixelSize()
{
var scaling = VisualRoot.RenderScaling;
return new PixelSize(Math.Max(1, (int)(Bounds.Width * scaling)),
Math.Max(1, (int)(Bounds.Height * scaling)));
}
protected virtual void OnOpenGlInit(GlInterface gl, int fb)

Loading…
Cancel
Save