From f3f26eb113ceec63e310115d34a8f02fe8e86b51 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 20 Feb 2024 11:47:47 +0600 Subject: [PATCH] Introduced a way to lease the underlying platform graphics context from Skia context (#14652) * Introduced a way to lease the underlying platform graphics context from Skia context * API suppression --- api/Avalonia.Skia.nupkg.xml | 10 ++++ src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 54 +++++++++++++++++-- src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs | 8 +++ .../Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs | 4 +- .../Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs | 4 +- .../ISkiaSharpApiLeaseFeature.cs | 8 +++ 6 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 api/Avalonia.Skia.nupkg.xml diff --git a/api/Avalonia.Skia.nupkg.xml b/api/Avalonia.Skia.nupkg.xml new file mode 100644 index 0000000000..6348b8f033 --- /dev/null +++ b/api/Avalonia.Skia.nupkg.xml @@ -0,0 +1,10 @@ + + + + + CP0006 + M:Avalonia.Skia.ISkiaSharpApiLease.TryLeasePlatformGraphicsApi + baseline/netstandard2.0/Avalonia.Skia.dll + target/netstandard2.0/Avalonia.Skia.dll + + diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 32a2edeebb..df82e7635a 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -99,6 +99,7 @@ namespace Avalonia.Skia private readonly DrawingContextImpl _context; private readonly SKMatrix _revertTransform; private bool _isDisposed; + private bool _leased; public ApiLease(DrawingContextImpl context) { @@ -107,11 +108,26 @@ namespace Avalonia.Skia _context._leased = true; } - public SKCanvas SkCanvas => _context.Canvas; + void CheckLease() + { + if (_leased) + throw new InvalidOperationException("The underlying graphics API is currently leased"); + } + + T CheckLease(T rv) + { + CheckLease(); + return rv; + } + + public SKCanvas SkCanvas => CheckLease(_context.Canvas); + // GrContext is accessible during the lease since one might want to wrap native resources + // Into Skia ones public GRContext? GrContext => _context.GrContext; - public SKSurface? SkSurface => _context.Surface; - public double CurrentOpacity => _context._currentOpacity; - + public SKSurface? SkSurface => CheckLease(_context.Surface); + public double CurrentOpacity => CheckLease(_context._currentOpacity); + + public void Dispose() { if (!_isDisposed) @@ -121,6 +137,36 @@ namespace Avalonia.Skia _isDisposed = true; } } + + class PlatformApiLease : ISkiaSharpPlatformGraphicsApiLease + { + private readonly ApiLease _parent; + + public PlatformApiLease(ApiLease parent, IPlatformGraphicsContext context) + { + _parent = parent; + _parent.GrContext?.Flush(); + Context = context; + _parent._leased = true; + } + + public void Dispose() + { + _parent._leased = false; + _parent.GrContext?.ResetContext(); + } + + public IPlatformGraphicsContext Context { get; } + } + + public ISkiaSharpPlatformGraphicsApiLease? TryLeasePlatformGraphicsApi() + { + CheckLease(); + if (_context._gpu is ISkiaGpuWithPlatformGraphicsContext gpu && + gpu.PlatformGraphicsContext is { } context) + return new PlatformApiLease(this, context); + return null; + } } } diff --git a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs index e6e30a1203..20e7adb334 100644 --- a/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/ISkiaGpu.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia.Metadata; using Avalonia.Platform; using SkiaSharp; @@ -24,6 +25,13 @@ namespace Avalonia.Skia /// An optional custom render session. ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session); } + + //TODO12: Merge into ISkiaGpu + [Unstable] + public interface ISkiaGpuWithPlatformGraphicsContext : ISkiaGpu + { + IPlatformGraphicsContext? PlatformGraphicsContext { get; } + } public interface ISkiaSurface : IDisposable { diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs index 8965f4fc7a..8e5573a465 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs @@ -2,11 +2,12 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Avalonia.Metal; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia.Metal; -internal class SkiaMetalGpu : ISkiaGpu +internal class SkiaMetalGpu : ISkiaGpu, ISkiaGpuWithPlatformGraphicsContext { private SkiaMetalApi _api = new(); private GRContext? _context; @@ -31,6 +32,7 @@ internal class SkiaMetalGpu : ISkiaGpu public bool IsLost => false; public IDisposable EnsureCurrent() => _device.EnsureCurrent(); + public IPlatformGraphicsContext? PlatformGraphicsContext => _device; public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable surfaces) { diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index d403855094..00752f8b2b 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -10,7 +10,8 @@ using static Avalonia.OpenGL.GlConsts; namespace Avalonia.Skia { - internal class GlSkiaGpu : ISkiaGpu, IOpenGlTextureSharingRenderInterfaceContextFeature + internal class GlSkiaGpu : ISkiaGpu, IOpenGlTextureSharingRenderInterfaceContextFeature, + ISkiaGpuWithPlatformGraphicsContext { private readonly GRContext _grContext; private readonly IGlContext _glContext; @@ -152,6 +153,7 @@ namespace Avalonia.Skia public bool IsLost => _glContext.IsLost; public IDisposable EnsureCurrent() => _glContext.EnsureCurrent(); + public IPlatformGraphicsContext? PlatformGraphicsContext => _glContext; public object? TryGetFeature(Type featureType) { diff --git a/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs b/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs index 66abd818e6..1e56ee520b 100644 --- a/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs +++ b/src/Skia/Avalonia.Skia/ISkiaSharpApiLeaseFeature.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Metadata; +using Avalonia.Platform; using SkiaSharp; namespace Avalonia.Skia; @@ -17,4 +18,11 @@ public interface ISkiaSharpApiLease : IDisposable GRContext? GrContext { get; } SKSurface? SkSurface { get; } double CurrentOpacity { get; } + ISkiaSharpPlatformGraphicsApiLease? TryLeasePlatformGraphicsApi(); +} + +[Unstable] +public interface ISkiaSharpPlatformGraphicsApiLease : IDisposable +{ + IPlatformGraphicsContext Context { get; } }