Browse Source

Properly snapshot GlSkiaImportedImage (#20053)

* Properly snapshot GlSkiaImportedImage

* Snapshot to an OpenGL texture

* Address review

* Share FBO between imported images
pull/20093/head
Julien Lebosquain 3 months ago
committed by GitHub
parent
commit
2a639ea00f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 147
      src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs

147
src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaExternalObjectsFeature.cs

@ -1,17 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media.Imaging;
using Avalonia.OpenGL; using Avalonia.OpenGL;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Rendering.Composition; using Avalonia.Rendering.Composition;
using SkiaSharp; using SkiaSharp;
using static Avalonia.OpenGL.GlConsts;
namespace Avalonia.Skia; namespace Avalonia.Skia;
internal class GlSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceContextFeature internal class GlSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceContextFeature, IGlSkiaFboProvider
{ {
private readonly GlSkiaGpu _gpu; private readonly GlSkiaGpu _gpu;
private readonly IGlContextExternalObjectsFeature? _feature; private readonly IGlContextExternalObjectsFeature? _feature;
private int _fbo;
public GlSkiaExternalObjectsFeature(GlSkiaGpu gpu, IGlContextExternalObjectsFeature? feature) public GlSkiaExternalObjectsFeature(GlSkiaGpu gpu, IGlContextExternalObjectsFeature? feature)
{ {
@ -32,7 +33,7 @@ internal class GlSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceCon
using (_gpu.EnsureCurrent()) using (_gpu.EnsureCurrent())
{ {
var image = _feature.ImportImage(handle, properties); var image = _feature.ImportImage(handle, properties);
return new GlSkiaImportedImage(_gpu, image); return new GlSkiaImportedImage(_gpu, this, image);
} }
} }
@ -42,7 +43,7 @@ internal class GlSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceCon
if (!img.Context.IsSharedWith(_gpu.GlContext)) if (!img.Context.IsSharedWith(_gpu.GlContext))
throw new InvalidOperationException("Contexts do not belong to the same share group"); throw new InvalidOperationException("Contexts do not belong to the same share group");
return new GlSkiaImportedImage(_gpu, img); return new GlSkiaImportedImage(_gpu, this, img);
} }
public IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle) public IPlatformRenderInterfaceImportedSemaphore ImportSemaphore(IPlatformHandle handle)
@ -59,10 +60,26 @@ internal class GlSkiaExternalObjectsFeature : IExternalObjectsRenderInterfaceCon
public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType) public CompositionGpuImportedImageSynchronizationCapabilities GetSynchronizationCapabilities(string imageHandleType)
=> _feature?.GetSynchronizationCapabilities(imageHandleType) ?? default; => _feature?.GetSynchronizationCapabilities(imageHandleType) ?? default;
public int Fbo
{
get
{
if (_fbo == 0)
_fbo = _gpu.GlContext.GlInterface.GenFramebuffer();
return _fbo;
}
}
public byte[]? DeviceUuid => _feature?.DeviceUuid; public byte[]? DeviceUuid => _feature?.DeviceUuid;
public byte[]? DeviceLuid => _feature?.DeviceLuid; public byte[]? DeviceLuid => _feature?.DeviceLuid;
} }
internal interface IGlSkiaFboProvider
{
int Fbo { get; }
}
internal class GlSkiaImportedSemaphore : IPlatformRenderInterfaceImportedSemaphore internal class GlSkiaImportedSemaphore : IPlatformRenderInterfaceImportedSemaphore
{ {
private readonly GlSkiaGpu _gpu; private readonly GlSkiaGpu _gpu;
@ -81,17 +98,20 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
{ {
private readonly GlSkiaSharedTextureForComposition? _sharedTexture; private readonly GlSkiaSharedTextureForComposition? _sharedTexture;
private readonly GlSkiaGpu _gpu; private readonly GlSkiaGpu _gpu;
private readonly IGlSkiaFboProvider _fboProvider;
private readonly IGlExternalImageTexture? _image; private readonly IGlExternalImageTexture? _image;
public GlSkiaImportedImage(GlSkiaGpu gpu, IGlExternalImageTexture image) public GlSkiaImportedImage(GlSkiaGpu gpu, IGlSkiaFboProvider fboProvider, IGlExternalImageTexture image)
{ {
_gpu = gpu; _gpu = gpu;
_fboProvider = fboProvider;
_image = image; _image = image;
} }
public GlSkiaImportedImage(GlSkiaGpu gpu, GlSkiaSharedTextureForComposition sharedTexture) public GlSkiaImportedImage(GlSkiaGpu gpu, IGlSkiaFboProvider fboProvider, GlSkiaSharedTextureForComposition sharedTexture)
{ {
_gpu = gpu; _gpu = gpu;
_fboProvider = fboProvider;
_sharedTexture = sharedTexture; _sharedTexture = sharedTexture;
} }
@ -109,21 +129,23 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
_ => SKColorType.Rgba8888 _ => SKColorType.Rgba8888
}; };
SKSurface? TryCreateSurface(int target, int textureId, int format, int width, int height, bool topLeft) SKImage? TryCreateImage(int target, int textureId, int format, int width, int height, bool topLeft)
{ {
var origin = topLeft ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft; var origin = topLeft ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft;
using var texture = new GRBackendTexture(width, height, false, using var texture = new GRBackendTexture(width, height, false,
new GRGlTextureInfo((uint)target, (uint)textureId, (uint)format)); new GRGlTextureInfo((uint)target, (uint)textureId, (uint)format));
var surf = SKSurface.Create(_gpu.GrContext, texture, origin, SKColorType.Rgba8888);
if (surf != null) var image = SKImage.FromAdoptedTexture(_gpu.GrContext, texture, origin, SKColorType.Rgba8888);
return surf; if (image is not null)
return image;
using var unformatted = new GRBackendTexture(width, height, false, using var unformatted = new GRBackendTexture(width, height, false,
new GRGlTextureInfo((uint)GlConsts.GL_TEXTURE_2D, (uint)textureId)); new GRGlTextureInfo((uint)target, (uint)textureId));
return SKSurface.Create(_gpu.GrContext, unformatted, origin, SKColorType.Rgba8888); return SKImage.FromAdoptedTexture(_gpu.GrContext, unformatted, origin, SKColorType.Rgba8888);
} }
IBitmapImpl TakeSnapshot() IBitmapImpl TakeSnapshot()
{ {
var width = _image?.Properties.Width ?? _sharedTexture!.Size.Width; var width = _image?.Properties.Width ?? _sharedTexture!.Size.Width;
@ -131,41 +153,41 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
var internalFormat = _image?.InternalFormat ?? _sharedTexture!.InternalFormat; var internalFormat = _image?.InternalFormat ?? _sharedTexture!.InternalFormat;
var textureId = _image?.TextureId ?? _sharedTexture!.TextureId; var textureId = _image?.TextureId ?? _sharedTexture!.TextureId;
var topLeft = _image?.Properties.TopLeftOrigin ?? false; var topLeft = _image?.Properties.TopLeftOrigin ?? false;
var textureType = _image?.TextureType ?? GlConsts.GL_TEXTURE_2D; var textureType = _image?.TextureType ?? GL_TEXTURE_2D;
var context = _gpu.GlContext;
IBitmapImpl rv; var snapshotTextureId = CopyToNewTexture(textureType, textureId, internalFormat, width, height);
using (var surf = TryCreateSurface(textureType, textureId, internalFormat, width, height, topLeft)) var snapshotImage = TryCreateImage(textureType, snapshotTextureId, internalFormat, width, height, topLeft);
if (snapshotImage is null)
{ {
if (surf == null) context.GlInterface.DeleteTexture(snapshotTextureId);
throw new OpenGlException("Unable to consume provided texture"); throw new OpenGlException("Unable to consume provided texture");
var snapshot = surf.Snapshot();
var context = _gpu.GlContext;
rv = new ImmutableBitmap(snapshot, () =>
{
IDisposable? restoreContext = null;
try
{
restoreContext = context.EnsureCurrent();
}
catch
{
// Ignore, context is likely dead
}
using (restoreContext)
{
snapshot.Dispose();
}
});
} }
var rv = new ImmutableBitmap(snapshotImage, () =>
{
IDisposable? restoreContext = null;
try
{
restoreContext = context.EnsureCurrent();
}
catch
{
// Ignore, context is likely dead
}
using (restoreContext)
{
snapshotImage.Dispose();
}
});
_gpu.GrContext.Flush(); _gpu.GrContext.Flush();
_gpu.GlContext.GlInterface.Flush(); context.GlInterface.Flush();
return rv; return rv;
} }
public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex) public IBitmapImpl SnapshotWithKeyedMutex(uint acquireIndex, uint releaseIndex)
{ {
if (_image is null) if (_image is null)
@ -240,4 +262,39 @@ internal class GlSkiaImportedImage : IPlatformRenderInterfaceImportedImage
using (_gpu.EnsureCurrent()) using (_gpu.EnsureCurrent())
return TakeSnapshot(); return TakeSnapshot();
} }
private int CopyToNewTexture(int textureType, int sourceTextureId, int internalFormat, int width, int height)
{
var gl = _gpu.GlContext.GlInterface;
using var _ = _gpu.EnsureCurrent();
// Snapshot current values
gl.GetIntegerv(GL_FRAMEBUFFER_BINDING, out var oldFbo);
gl.GetIntegerv(GL_SCISSOR_TEST, out var oldScissorTest);
// Bind source texture
gl.BindFramebuffer(GL_FRAMEBUFFER, _fboProvider.Fbo);
gl.Disable(GL_SCISSOR_TEST);
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureType, sourceTextureId, 0);
// Create destination texture
var destTextureId = gl.GenTexture();
gl.BindTexture(textureType, destTextureId);
gl.TexImage2D(textureType, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, IntPtr.Zero);
// Copy
gl.CopyTexSubImage2D(textureType, 0, 0, 0, 0, 0, width, height);
// Flush
gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureType, 0, 0);
gl.Flush();
// Restore old values
gl.BindFramebuffer(GL_FRAMEBUFFER, oldFbo);
if (oldScissorTest != 0)
gl.Enable(GL_SCISSOR_TEST);
return destTextureId;
}
} }

Loading…
Cancel
Save