A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

145 lines
4.9 KiB

using System;
using Avalonia.OpenGL;
using CoreAnimation;
using OpenGLES;
namespace Avalonia.iOS
{
internal class LayerFbo
{
private readonly EAGLContext _context;
private readonly GlInterface _gl;
private readonly CAEAGLLayer _layer;
private int _framebuffer;
private int _renderbuffer;
private int _depthBuffer;
private bool _disposed;
private LayerFbo(EAGLContext context, GlInterface gl, CAEAGLLayer layer, int framebuffer, int renderbuffer, int depthBuffer)
{
_context = context;
_gl = gl;
_layer = layer;
_framebuffer = framebuffer;
_renderbuffer = renderbuffer;
_depthBuffer = depthBuffer;
}
public static LayerFbo TryCreate(EAGLContext context, GlInterface gl, CAEAGLLayer layer)
{
if (context != EAGLContext.CurrentContext)
return null;
var rb = gl.GenRenderbuffer();
gl.BindRenderbuffer(GlConsts.GL_RENDERBUFFER, rb);
context.RenderBufferStorage(GlConsts.GL_RENDERBUFFER, layer);
var fb = gl.GenFramebuffer();
gl.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, fb);
gl.FramebufferRenderbuffer(GlConsts.GL_FRAMEBUFFER, GlConsts.GL_COLOR_ATTACHMENT0, GlConsts.GL_RENDERBUFFER, rb);
gl.GetRenderbufferParameteriv(GlConsts.GL_RENDERBUFFER, GlConsts.GL_RENDERBUFFER_WIDTH, out var w);
gl.GetRenderbufferParameteriv(GlConsts.GL_RENDERBUFFER, GlConsts.GL_RENDERBUFFER_HEIGHT, out var h);
var db = gl.GenRenderbuffer();
//GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthBuffer);
//GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferInternalFormat.DepthComponent16, w, h);
gl.FramebufferRenderbuffer(GlConsts.GL_FRAMEBUFFER, GlConsts.GL_DEPTH_ATTACHMENT, GlConsts.GL_RENDERBUFFER, db);
var frameBufferError = gl.CheckFramebufferStatus(GlConsts.GL_FRAMEBUFFER);
if(frameBufferError != GlConsts.GL_FRAMEBUFFER_COMPLETE)
{
gl.DeleteFramebuffer(fb);
gl.DeleteRenderbuffer(db);
gl.DeleteRenderbuffer(rb);
return null;
}
return new LayerFbo(context, gl, layer, fb, rb, db)
{
Width = w,
Height = h
};
}
public int Width { get; private set; }
public int Height { get; private set; }
public void Bind()
{
_gl.BindFramebuffer(GlConsts.GL_FRAMEBUFFER, _framebuffer);
}
public void Present()
{
Bind();
var success = _context.PresentRenderBuffer(GlConsts.GL_RENDERBUFFER);
}
public void Dispose()
{
if(_disposed)
return;
_disposed = true;
_gl.DeleteFramebuffer(_framebuffer);
_gl.DeleteRenderbuffer(_depthBuffer);
_gl.DeleteRenderbuffer(_renderbuffer);
if (_context != EAGLContext.CurrentContext)
throw new InvalidOperationException("Associated EAGLContext is not current");
}
}
class SizeSynchronizedLayerFbo : IDisposable
{
private readonly EAGLContext _context;
private readonly GlInterface _gl;
private readonly CAEAGLLayer _layer;
private LayerFbo _fbo;
private double _oldLayerWidth, _oldLayerHeight, _oldLayerScale;
public SizeSynchronizedLayerFbo(EAGLContext context, GlInterface gl, CAEAGLLayer layer)
{
_context = context;
_gl = gl;
_layer = layer;
}
public bool Sync()
{
if (_fbo != null
&& _oldLayerWidth == _layer.Bounds.Width
&& _oldLayerHeight == _layer.Bounds.Height
&& _oldLayerScale == _layer.ContentsScale)
return true;
_fbo?.Dispose();
_fbo = null;
_fbo = LayerFbo.TryCreate(_context, _gl, _layer);
_oldLayerWidth = _layer.Bounds.Width;
_oldLayerHeight = _layer.Bounds.Height;
_oldLayerScale = _layer.ContentsScale;
return _fbo != null;
}
public void Dispose()
{
if (_context != EAGLContext.CurrentContext)
throw new InvalidOperationException("Associated EAGLContext is not current");
_fbo?.Dispose();
_fbo = null;
}
public void Bind()
{
if(!Sync())
throw new InvalidOperationException("Unable to create a render target");
_fbo.Bind();
}
public void Present() => _fbo.Present();
public int Width => _fbo?.Width ?? 0;
public int Height => _fbo?.Height ?? 0;
public double Scaling => _oldLayerScale;
}
}