csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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.
137 lines
4.6 KiB
137 lines
4.6 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Avalonia.Metal;
|
|
using Avalonia.Platform;
|
|
using SkiaSharp;
|
|
|
|
namespace Avalonia.Skia.Metal;
|
|
|
|
internal class SkiaMetalGpu : ISkiaGpu
|
|
{
|
|
private GRContext? _context;
|
|
private readonly IMetalDevice _device;
|
|
private readonly SkiaMetalExternalObjectsFeature? _externalObjects;
|
|
|
|
internal GRContext GrContext => _context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu));
|
|
|
|
public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes)
|
|
{
|
|
_context = GRContext.CreateMetal(
|
|
new GRMtlBackendContext { DeviceHandle = device.Device, QueueHandle = device.CommandQueue, },
|
|
new GRContextOptions { AvoidStencilBuffers = true })
|
|
?? throw new InvalidOperationException("Unable to create GRContext from Metal device.");
|
|
_device = device;
|
|
if (device.TryGetFeature<IMetalExternalObjectsFeature>() is { } externalObjects)
|
|
_externalObjects = new SkiaMetalExternalObjectsFeature(this, externalObjects);
|
|
if (maxResourceBytes.HasValue)
|
|
_context.SetResourceCacheLimit(maxResourceBytes.Value);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_context?.Dispose();
|
|
_context = null;
|
|
}
|
|
|
|
public object? TryGetFeature(Type featureType)
|
|
{
|
|
if (featureType == typeof(IExternalObjectsHandleWrapRenderInterfaceContextFeature))
|
|
return _device.TryGetFeature(featureType);
|
|
if (featureType == typeof(IExternalObjectsRenderInterfaceContextFeature))
|
|
return _externalObjects;
|
|
return null;
|
|
}
|
|
|
|
public bool IsLost => false;
|
|
public IDisposable EnsureCurrent() => _device.EnsureCurrent();
|
|
public IPlatformGraphicsContext? PlatformGraphicsContext => _device;
|
|
|
|
public IScopedResource<GRContext> TryGetGrContext() =>
|
|
ScopedResource<GRContext>.Create(GrContext, EnsureCurrent().Dispose);
|
|
|
|
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(PixelSize? expectedPixelSize)
|
|
{
|
|
// TODO: use expectedPixelSize
|
|
var session = (_target ?? throw new ObjectDisposedException(nameof(SkiaMetalRenderTarget))).BeginRendering();
|
|
var backendTarget = new GRBackendRenderTarget(session.Size.Width, session.Size.Height,
|
|
new GRMtlTextureInfo(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;
|
|
}
|
|
|