|
|
@ -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; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|