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.
 
 
 

171 lines
5.8 KiB

using System;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.Platform;
using Avalonia.Reactive;
using Avalonia.Rendering.Composition;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.OpenGL.Controls;
internal class OpenGlControlBaseResources : IAsyncDisposable
{
private int _depthBuffer;
public int Fbo { get; private set; }
private PixelSize _depthBufferSize;
public CompositionDrawingSurface Surface { get; }
private readonly CompositionOpenGlSwapchain _swapchain;
public IGlContext Context { get; private set; }
public static OpenGlControlBaseResources? TryCreate(CompositionDrawingSurface surface,
ICompositionGpuInterop interop,
IOpenGlTextureSharingRenderInterfaceContextFeature feature)
{
IGlContext? context;
try
{
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);
return null;
}
if (context == null)
{
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to initialize OpenGL: unable to create additional OpenGL context.");
return null;
}
return new OpenGlControlBaseResources(context, surface, interop, feature, null);
}
public static OpenGlControlBaseResources? TryCreate(IGlContext context, CompositionDrawingSurface surface,
ICompositionGpuInterop interop, IGlContextExternalObjectsFeature externalObjects)
{
if (!interop.SupportedImageHandleTypes.Contains(KnownPlatformGraphicsExternalImageHandleTypes
.D3D11TextureGlobalSharedHandle)
|| !externalObjects.SupportedExportableExternalImageTypes.Contains(
KnownPlatformGraphicsExternalImageHandleTypes.D3D11TextureGlobalSharedHandle))
return null;
return new OpenGlControlBaseResources(context, surface, interop, null, externalObjects);
}
private OpenGlControlBaseResources(IGlContext context,
CompositionDrawingSurface surface,
ICompositionGpuInterop interop,
IOpenGlTextureSharingRenderInterfaceContextFeature? feature,
IGlContextExternalObjectsFeature? externalObjects
)
{
Context = context;
Surface = surface;
using (context.MakeCurrent())
Fbo = context.GlInterface.GenFramebuffer();
_swapchain =
feature != null ?
new CompositionOpenGlSwapchain(context, interop, Surface, feature) :
new CompositionOpenGlSwapchain(context, interop, Surface, externalObjects);
}
private void UpdateDepthRenderbuffer(PixelSize size)
{
if (size == _depthBufferSize && _depthBuffer != 0)
return;
var gl = Context.GlInterface;
gl.GetIntegerv(GL_RENDERBUFFER_BINDING, out var oldRenderBuffer);
if (_depthBuffer != 0) gl.DeleteRenderbuffer(_depthBuffer);
_depthBuffer = gl.GenRenderbuffer();
gl.BindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
gl.RenderbufferStorage(GL_RENDERBUFFER,
Context.Version.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);
_depthBufferSize = size;
}
public IDisposable BeginDraw(PixelSize size)
{
var restoreContext = Context.EnsureCurrent();
IDisposable? imagePresent = null;
var success = false;
try
{
var gl = Context.GlInterface;
Context.GlInterface.BindFramebuffer(GL_FRAMEBUFFER, Fbo);
UpdateDepthRenderbuffer(size);
imagePresent = _swapchain.BeginDraw(size, out var texture);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.TextureId, 0);
var status = gl.CheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
int code = gl.GetError();
Logger.TryGet(LogEventLevel.Error, "OpenGL")?.Log("OpenGlControlBase",
"Unable to configure OpenGL FBO: {code}", code);
throw OpenGlException.GetFormattedException("Unable to configure OpenGL FBO", code);
}
success = true;
return Disposable.Create(() =>
{
try
{
Context.GlInterface.Flush();
imagePresent.Dispose();
}
finally
{
restoreContext.Dispose();
}
});
}
finally
{
if (!success)
{
imagePresent?.Dispose();
restoreContext.Dispose();
}
}
}
public async ValueTask DisposeAsync()
{
if (Context is { IsLost: false })
{
try
{
using (Context.MakeCurrent())
{
var gl = Context.GlInterface;
if (Fbo != 0)
gl.DeleteFramebuffer(Fbo);
Fbo = 0;
if (_depthBuffer != 0)
gl.DeleteRenderbuffer(_depthBuffer);
_depthBuffer = 0;
}
}
catch
{
//
}
Surface.Dispose();
await _swapchain.DisposeAsync();
Context.Dispose();
Context = null!;
}
}
}