committed by
GitHub
55 changed files with 1290 additions and 245 deletions
@ -0,0 +1,11 @@ |
|||||
|
#import <AppKit/AppKit.h> |
||||
|
|
||||
|
class CppAutoreleasePool |
||||
|
{ |
||||
|
void* _pool; |
||||
|
public: |
||||
|
CppAutoreleasePool(); |
||||
|
~CppAutoreleasePool(); |
||||
|
}; |
||||
|
|
||||
|
#define START_ARP_CALL CppAutoreleasePool __autoreleasePool |
||||
@ -0,0 +1,161 @@ |
|||||
|
#import <AppKit/AppKit.h> |
||||
|
#import <Metal/Metal.h> |
||||
|
#import <QuartzCore/QuartzCore.h> |
||||
|
#include "common.h" |
||||
|
#include "rendertarget.h" |
||||
|
|
||||
|
class AvnMetalDevice : public ComSingleObject<IAvnMetalDevice, &IID_IAvnMetalDevice> |
||||
|
{ |
||||
|
public: |
||||
|
id<MTLDevice> device; |
||||
|
id<MTLCommandQueue> queue; |
||||
|
FORWARD_IUNKNOWN() |
||||
|
|
||||
|
void *GetDevice() override { |
||||
|
return (__bridge void*) device; |
||||
|
} |
||||
|
|
||||
|
void *GetQueue() override { |
||||
|
return (__bridge void*) queue; |
||||
|
} |
||||
|
|
||||
|
AvnMetalDevice(id <MTLDevice> device, id <MTLCommandQueue> queue) : device(device), queue(queue) { |
||||
|
} |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
|
||||
|
class AvnMetalRenderSession : public ComSingleObject<IAvnMetalRenderingSession, &IID_IAvnMetalRenderingSession> |
||||
|
{ |
||||
|
id<CAMetalDrawable> _drawable; |
||||
|
id<MTLCommandQueue> _queue; |
||||
|
id<MTLTexture> _texture; |
||||
|
CAMetalLayer* _layer; |
||||
|
AvnPixelSize _size; |
||||
|
double _scaling; |
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
|
||||
|
AvnMetalRenderSession(AvnMetalDevice* device, CAMetalLayer* layer, id <CAMetalDrawable> drawable, const AvnPixelSize &size, double scaling) |
||||
|
: _drawable(drawable), _size(size), _scaling(scaling), _queue(device->queue), |
||||
|
_texture([drawable texture]) { |
||||
|
_layer = layer; |
||||
|
} |
||||
|
|
||||
|
HRESULT GetPixelSize(AvnPixelSize *ret) override { |
||||
|
*ret = _size; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
double GetScaling() override { |
||||
|
return _scaling; |
||||
|
} |
||||
|
|
||||
|
void *GetTexture() override { |
||||
|
return (__bridge void*) _texture; |
||||
|
} |
||||
|
|
||||
|
~AvnMetalRenderSession() |
||||
|
{ |
||||
|
auto buffer = [_queue commandBuffer]; |
||||
|
[buffer presentDrawable: _drawable]; |
||||
|
[buffer commit]; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
class AvnMetalRenderTarget : public ComSingleObject<IAvnMetalRenderTarget, &IID_IAvnMetalRenderTarget> |
||||
|
{ |
||||
|
CAMetalLayer* _layer; |
||||
|
double _scaling = 1; |
||||
|
AvnPixelSize _size = {1,1}; |
||||
|
ComPtr<AvnMetalDevice> _device; |
||||
|
public: |
||||
|
double PendingScaling = 1; |
||||
|
AvnPixelSize PendingSize = {1,1}; |
||||
|
FORWARD_IUNKNOWN() |
||||
|
AvnMetalRenderTarget(CAMetalLayer* layer, ComPtr<AvnMetalDevice> device) |
||||
|
{ |
||||
|
_layer = layer; |
||||
|
_device = device; |
||||
|
} |
||||
|
|
||||
|
HRESULT BeginDrawing(IAvnMetalRenderingSession **ret) override { |
||||
|
if([NSThread isMainThread]) |
||||
|
{ |
||||
|
// Flush all existing rendering |
||||
|
auto buffer = [_device->queue commandBuffer]; |
||||
|
[buffer commit]; |
||||
|
[buffer waitUntilCompleted]; |
||||
|
_size = PendingSize; |
||||
|
_scaling= PendingScaling; |
||||
|
CGSize layerSize = {(CGFloat)_size.Width, (CGFloat)_size.Height}; |
||||
|
|
||||
|
[_layer setDrawableSize: layerSize]; |
||||
|
} |
||||
|
auto drawable = [_layer nextDrawable]; |
||||
|
if(drawable == nil) |
||||
|
{ |
||||
|
ret = nil; |
||||
|
return E_FAIL; |
||||
|
} |
||||
|
*ret = new AvnMetalRenderSession(_device, _layer, drawable, _size, _scaling); |
||||
|
return 0; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
@implementation MetalRenderTarget |
||||
|
{ |
||||
|
ComPtr<AvnMetalDevice> _device; |
||||
|
CAMetalLayer* _layer; |
||||
|
ComPtr<AvnMetalRenderTarget> _target; |
||||
|
} |
||||
|
- (MetalRenderTarget *)initWithDevice:(IAvnMetalDevice *)device { |
||||
|
_device = dynamic_cast<AvnMetalDevice*>(device); |
||||
|
_layer = [CAMetalLayer new]; |
||||
|
_layer.device = _device->device; |
||||
|
_target.setNoAddRef(new AvnMetalRenderTarget(_layer, _device)); |
||||
|
return self; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
-(void) getRenderTarget: (IAvnMetalRenderTarget**) ppv |
||||
|
{ |
||||
|
*ppv = static_cast<IAvnMetalRenderTarget*>(_target.getRetainedReference()); |
||||
|
} |
||||
|
|
||||
|
- (void)resize:(AvnPixelSize)size withScale:(float)scale { |
||||
|
CGSize layerSize = {(CGFloat)size.Width, (CGFloat)size.Height}; |
||||
|
_target->PendingScaling = scale; |
||||
|
_target->PendingSize = size; |
||||
|
[_layer setNeedsDisplay]; |
||||
|
} |
||||
|
|
||||
|
- (CALayer *)layer { |
||||
|
return _layer; |
||||
|
} |
||||
|
@end |
||||
|
|
||||
|
|
||||
|
class AvnMetalDisplay : public ComSingleObject<IAvnMetalDisplay, &IID_IAvnMetalDisplay> |
||||
|
{ |
||||
|
public: |
||||
|
FORWARD_IUNKNOWN() |
||||
|
HRESULT CreateDevice(IAvnMetalDevice **ret) override { |
||||
|
|
||||
|
auto device = MTLCreateSystemDefaultDevice(); |
||||
|
if(device == nil) { |
||||
|
ret = nil; |
||||
|
return E_FAIL; |
||||
|
} |
||||
|
auto queue = [device newCommandQueue]; |
||||
|
*ret = new AvnMetalDevice(device, queue); |
||||
|
return S_OK; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
static AvnMetalDisplay* _display = new AvnMetalDisplay(); |
||||
|
|
||||
|
extern IAvnMetalDisplay* GetMetalDisplay() |
||||
|
{ |
||||
|
return _display; |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
#include "noarc.h" |
||||
|
|
||||
|
CppAutoreleasePool::CppAutoreleasePool() |
||||
|
{ |
||||
|
_pool = [[NSAutoreleasePool alloc] init]; |
||||
|
} |
||||
|
|
||||
|
CppAutoreleasePool::~CppAutoreleasePool() { |
||||
|
auto ptr = (NSAutoreleasePool*)_pool; |
||||
|
[ptr release]; |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia; |
||||
|
|
||||
|
public class RenderTargetNotReadyException : Exception |
||||
|
{ |
||||
|
public RenderTargetNotReadyException() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public RenderTargetNotReadyException(string message) |
||||
|
: base(message) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public RenderTargetNotReadyException(Exception innerException) |
||||
|
: base(null, innerException) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public RenderTargetNotReadyException(string message, Exception innerException) |
||||
|
: base(message, innerException) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks> |
||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" /> |
||||
|
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<Import Project="..\..\build\DevAnalyzers.props" /> |
||||
|
<Import Project="..\..\build\TrimmingEnable.props" /> |
||||
|
<Import Project="..\..\build\NullableEnable.props" /> |
||||
|
</Project> |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Metadata; |
||||
|
using Avalonia.Platform; |
||||
|
|
||||
|
namespace Avalonia.Metal; |
||||
|
|
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalDevice : IPlatformGraphicsContext |
||||
|
{ |
||||
|
IntPtr Device { get; } |
||||
|
IntPtr CommandQueue { get; } |
||||
|
} |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalPlatformSurface |
||||
|
{ |
||||
|
IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device); |
||||
|
} |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalPlatformSurfaceRenderTarget : IDisposable |
||||
|
{ |
||||
|
IMetalPlatformSurfaceRenderingSession BeginRendering(); |
||||
|
} |
||||
|
|
||||
|
[PrivateApi] |
||||
|
public interface IMetalPlatformSurfaceRenderingSession : IDisposable |
||||
|
{ |
||||
|
IntPtr Texture { get; } |
||||
|
PixelSize Size { get; } |
||||
|
double Scaling { get; } |
||||
|
bool IsYFlipped { get; } |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using Avalonia.Native.Interop; |
||||
|
using Avalonia.Threading; |
||||
|
using MicroCom.Runtime; |
||||
|
|
||||
|
namespace Avalonia.Native; |
||||
|
|
||||
|
class AvnDispatcher : NativeCallbackBase, IAvnDispatcher |
||||
|
{ |
||||
|
public void Post(IAvnActionCallback cb) |
||||
|
{ |
||||
|
var callback = cb.CloneReference(); |
||||
|
Dispatcher.UIThread.Post(() => |
||||
|
{ |
||||
|
using (callback) |
||||
|
callback.Run(); |
||||
|
}, DispatcherPriority.Send); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Metal; |
||||
|
using Avalonia.Native.Interop; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Threading; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Native; |
||||
|
|
||||
|
class MetalPlatformGraphics : IPlatformGraphics |
||||
|
{ |
||||
|
private readonly IAvnMetalDisplay _display; |
||||
|
|
||||
|
public MetalPlatformGraphics(IAvaloniaNativeFactory factory) |
||||
|
{ |
||||
|
_display = factory.ObtainMetalDisplay(); |
||||
|
} |
||||
|
public bool UsesSharedContext => false; |
||||
|
public IPlatformGraphicsContext CreateContext() => new MetalDevice(_display.CreateDevice()); |
||||
|
|
||||
|
public IPlatformGraphicsContext GetSharedContext() => throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
class MetalDevice : IMetalDevice |
||||
|
{ |
||||
|
public IAvnMetalDevice Native { get; private set; } |
||||
|
private DisposableLock _syncRoot = new(); |
||||
|
|
||||
|
|
||||
|
public MetalDevice(IAvnMetalDevice native) |
||||
|
{ |
||||
|
Native = native; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Native?.Dispose(); |
||||
|
Native = null; |
||||
|
} |
||||
|
|
||||
|
public object TryGetFeature(Type featureType) => null; |
||||
|
|
||||
|
public bool IsLost => false; |
||||
|
|
||||
|
public IDisposable EnsureCurrent() => _syncRoot.Lock(); |
||||
|
|
||||
|
public IntPtr Device => Native.Device; |
||||
|
public IntPtr CommandQueue => Native.Queue; |
||||
|
} |
||||
|
|
||||
|
class MetalPlatformSurface : IMetalPlatformSurface |
||||
|
{ |
||||
|
private readonly IAvnWindowBase _window; |
||||
|
|
||||
|
public MetalPlatformSurface(IAvnWindowBase window) |
||||
|
{ |
||||
|
_window = window; |
||||
|
} |
||||
|
public IMetalPlatformSurfaceRenderTarget CreateMetalRenderTarget(IMetalDevice device) |
||||
|
{ |
||||
|
if (!Dispatcher.UIThread.CheckAccess()) |
||||
|
throw new RenderTargetNotReadyException(); |
||||
|
|
||||
|
var dev = (MetalDevice)device; |
||||
|
var target = _window.CreateMetalRenderTarget(dev.Native); |
||||
|
return new MetalRenderTarget(target); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class MetalRenderTarget : IMetalPlatformSurfaceRenderTarget |
||||
|
{ |
||||
|
private IAvnMetalRenderTarget _native; |
||||
|
|
||||
|
public MetalRenderTarget(IAvnMetalRenderTarget native) |
||||
|
{ |
||||
|
_native = native; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_native?.Dispose(); |
||||
|
_native = null; |
||||
|
} |
||||
|
|
||||
|
public IMetalPlatformSurfaceRenderingSession BeginRendering() |
||||
|
{ |
||||
|
var session = _native.BeginDrawing(); |
||||
|
return new MetalDrawingSession(session); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal class MetalDrawingSession : IMetalPlatformSurfaceRenderingSession |
||||
|
{ |
||||
|
private IAvnMetalRenderingSession _session; |
||||
|
|
||||
|
public MetalDrawingSession(IAvnMetalRenderingSession session) |
||||
|
{ |
||||
|
_session = session; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_session?.Dispose(); |
||||
|
_session = null; |
||||
|
} |
||||
|
|
||||
|
public IntPtr Texture => _session.Texture; |
||||
|
public PixelSize Size |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
var size = _session.PixelSize; |
||||
|
return new(size.Width, size.Height); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double Scaling => _session.Scaling; |
||||
|
public bool IsYFlipped => false; |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics.CodeAnalysis; |
||||
|
using System.Reflection; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
using SkiaSharp; |
||||
|
using BindingFlags = System.Reflection.BindingFlags; |
||||
|
|
||||
|
namespace Avalonia.Skia.Metal; |
||||
|
|
||||
|
internal unsafe class SkiaMetalApi |
||||
|
{ |
||||
|
delegate* unmanaged[Stdcall] <IntPtr, IntPtr, IntPtr, IntPtr> _gr_direct_context_make_metal_with_options; |
||||
|
private delegate* unmanaged[Stdcall]<int, int, int, GRMtlTextureInfoNative*, IntPtr> |
||||
|
_gr_backendrendertarget_new_metal; |
||||
|
private readonly ConstructorInfo _contextCtor; |
||||
|
private readonly MethodInfo _contextOptionsToNative; |
||||
|
private readonly ConstructorInfo _renderTargetCtor; |
||||
|
|
||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(GRContext))] |
||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof(GRBackendRenderTarget))] |
||||
|
[DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, typeof(GRContextOptions))] |
||||
|
public SkiaMetalApi() |
||||
|
{ |
||||
|
// Make sure that skia is loaded
|
||||
|
GC.KeepAlive(new SKPaint()); |
||||
|
|
||||
|
var loader = AvaloniaLocator.Current.GetRequiredService<IDynamicLibraryLoader>(); |
||||
|
#if NET6_0_OR_GREATER
|
||||
|
var dll = NativeLibrary.Load("libSkiaSharp", typeof(SKPaint).Assembly, null); |
||||
|
#else
|
||||
|
var dll = loader.LoadLibrary("libSkiaSharp"); |
||||
|
#endif
|
||||
|
_gr_direct_context_make_metal_with_options = (delegate* unmanaged[Stdcall] <IntPtr, IntPtr, IntPtr, IntPtr>) |
||||
|
loader.GetProcAddress(dll, "gr_direct_context_make_metal_with_options", false); |
||||
|
_gr_backendrendertarget_new_metal = |
||||
|
(delegate* unmanaged[Stdcall]<int, int, int, GRMtlTextureInfoNative*, IntPtr>) |
||||
|
loader.GetProcAddress(dll, "gr_backendrendertarget_new_metal", false); |
||||
|
|
||||
|
_contextCtor = typeof(GRContext).GetConstructor( |
||||
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, |
||||
|
new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); |
||||
|
|
||||
|
|
||||
|
_renderTargetCtor = typeof(GRBackendRenderTarget).GetConstructor( |
||||
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, |
||||
|
new[] { typeof(IntPtr), typeof(bool) }, null) ?? throw new MissingMemberException("GRContext.ctor(IntPtr,bool)"); |
||||
|
|
||||
|
_contextOptionsToNative = typeof(GRContextOptions).GetMethod("ToNative", |
||||
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) |
||||
|
?? throw new MissingMemberException("GRContextOptions.ToNative()"); |
||||
|
} |
||||
|
|
||||
|
public GRContext CreateContext(IntPtr device, IntPtr queue, GRContextOptions? options) |
||||
|
{ |
||||
|
options ??= new(); |
||||
|
var nativeOptions = _contextOptionsToNative.Invoke(options, null)!; |
||||
|
var pOptions = Marshal.AllocHGlobal(Marshal.SizeOf(nativeOptions)); |
||||
|
Marshal.StructureToPtr(nativeOptions, pOptions, false); |
||||
|
var context = _gr_direct_context_make_metal_with_options(device, queue, pOptions); |
||||
|
Marshal.FreeHGlobal(pOptions); |
||||
|
if (context == IntPtr.Zero) |
||||
|
throw new ArgumentException(); |
||||
|
return (GRContext)_contextCtor.Invoke(new object[] { context, true }); |
||||
|
} |
||||
|
|
||||
|
internal struct GRMtlTextureInfoNative |
||||
|
{ |
||||
|
public IntPtr Texture; |
||||
|
} |
||||
|
|
||||
|
public GRBackendRenderTarget CreateBackendRenderTarget(int width, int height, int samples, IntPtr texture) |
||||
|
{ |
||||
|
var info = new GRMtlTextureInfoNative() { Texture = texture }; |
||||
|
var target = _gr_backendrendertarget_new_metal(width, height, samples, &info); |
||||
|
if (target == IntPtr.Zero) |
||||
|
throw new InvalidOperationException("Unable to create GRBackendRenderTarget"); |
||||
|
return (GRBackendRenderTarget)_renderTargetCtor.Invoke(new object[] { target, true }); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using Avalonia.Metal; |
||||
|
using SkiaSharp; |
||||
|
|
||||
|
namespace Avalonia.Skia.Metal; |
||||
|
|
||||
|
internal class SkiaMetalGpu : ISkiaGpu |
||||
|
{ |
||||
|
private SkiaMetalApi _api = new(); |
||||
|
private GRContext? _context; |
||||
|
private readonly IMetalDevice _device; |
||||
|
|
||||
|
public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes) |
||||
|
{ |
||||
|
_context = _api.CreateContext(device.Device, device.CommandQueue, |
||||
|
new GRContextOptions() { AvoidStencilBuffers = true }); |
||||
|
_device = device; |
||||
|
if (maxResourceBytes.HasValue) |
||||
|
_context.SetResourceCacheLimit(maxResourceBytes.Value); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_context?.Dispose(); |
||||
|
_context = null; |
||||
|
} |
||||
|
|
||||
|
public object? TryGetFeature(Type featureType) => null; |
||||
|
|
||||
|
public bool IsLost => false; |
||||
|
public IDisposable EnsureCurrent() => _device.EnsureCurrent(); |
||||
|
|
||||
|
public ISkiaGpuRenderTarget? TryCreateRenderTarget(IEnumerable<object> surfaces) |
||||
|
{ |
||||
|
foreach (var surface in surfaces) |
||||
|
{ |
||||
|
if (surface is IMetalPlatformSurface metalSurface) |
||||
|
{ |
||||
|
var target = metalSurface.CreateMetalRenderTarget(_device); |
||||
|
return new SkiaMetalRenderTarget(this, target); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public class SkiaMetalRenderTarget : ISkiaGpuRenderTarget |
||||
|
{ |
||||
|
private readonly SkiaMetalGpu _gpu; |
||||
|
private IMetalPlatformSurfaceRenderTarget? _target; |
||||
|
|
||||
|
public SkiaMetalRenderTarget(SkiaMetalGpu gpu, IMetalPlatformSurfaceRenderTarget target) |
||||
|
{ |
||||
|
_gpu = gpu; |
||||
|
_target = target; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_target?.Dispose(); |
||||
|
_target = null; |
||||
|
} |
||||
|
|
||||
|
public ISkiaGpuRenderSession BeginRenderingSession() |
||||
|
{ |
||||
|
var session = (_target ?? throw new ObjectDisposedException(nameof(SkiaMetalRenderTarget))).BeginRendering(); |
||||
|
var backendTarget = _gpu._api.CreateBackendRenderTarget(session.Size.Width, session.Size.Height, |
||||
|
1, session.Texture); |
||||
|
|
||||
|
var surface = SKSurface.Create(_gpu._context!, backendTarget, |
||||
|
session.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft, |
||||
|
SKColorType.Bgra8888); |
||||
|
|
||||
|
return new SkiaMetalRenderSession(_gpu, surface, session); |
||||
|
} |
||||
|
|
||||
|
public bool IsCorrupted => false; |
||||
|
} |
||||
|
|
||||
|
internal class SkiaMetalRenderSession : ISkiaGpuRenderSession |
||||
|
{ |
||||
|
private readonly SkiaMetalGpu _gpu; |
||||
|
private SKSurface? _surface; |
||||
|
private IMetalPlatformSurfaceRenderingSession? _session; |
||||
|
|
||||
|
public SkiaMetalRenderSession(SkiaMetalGpu gpu, |
||||
|
SKSurface surface, |
||||
|
IMetalPlatformSurfaceRenderingSession session) |
||||
|
{ |
||||
|
_gpu = gpu; |
||||
|
_surface = surface; |
||||
|
_session = session; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_surface?.Canvas.Flush(); |
||||
|
_surface?.Flush(); |
||||
|
_gpu._context?.Flush(); |
||||
|
|
||||
|
_surface?.Dispose(); |
||||
|
_surface = null; |
||||
|
_session?.Dispose(); |
||||
|
_session = null; |
||||
|
} |
||||
|
|
||||
|
public GRContext GrContext => _gpu._context!; |
||||
|
public SKSurface SkSurface => _surface!; |
||||
|
public double ScaleFactor => _session!.Scaling; |
||||
|
|
||||
|
public GRSurfaceOrigin SurfaceOrigin => |
||||
|
_session!.IsYFlipped ? GRSurfaceOrigin.BottomLeft : GRSurfaceOrigin.TopLeft; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public ISkiaSurface? TryCreateSurface(PixelSize size, ISkiaGpuRenderSession? session) => null; |
||||
|
} |
||||
Loading…
Reference in new issue