From 9808817e31a1fec0bc361df1a29b9ecd66639a5b Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Mon, 15 Jun 2026 13:46:27 +0200 Subject: [PATCH] Enable stencil buffers by default + add configurable option (#21586) --- src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs | 5 +++-- src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs | 5 +++-- src/Skia/Avalonia.Skia/Gpu/Vulkan/VulkanSkiaGpu.cs | 6 ++++-- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 10 ++++++---- src/Skia/Avalonia.Skia/SkiaOptions.cs | 6 ++++++ src/Skia/Avalonia.Skia/SkiaPlatform.cs | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs index ccb3a51705..05bcd9fa42 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Metal/SkiaMetalGpu.cs @@ -15,11 +15,12 @@ internal class SkiaMetalGpu : ISkiaGpu internal GRContext GrContext => _context ?? throw new ObjectDisposedException(nameof(SkiaMetalGpu)); - public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes) + public SkiaMetalGpu(IMetalDevice device, long? maxResourceBytes, bool? useStencilBuffers = null) { + var avoidStencilBuffers = useStencilBuffers == false; _context = GRContext.CreateMetal( new GRMtlBackendContext { DeviceHandle = device.Device, QueueHandle = device.CommandQueue, }, - new GRContextOptions { AvoidStencilBuffers = true }) + new GRContextOptions { AvoidStencilBuffers = avoidStencilBuffers }) ?? throw new InvalidOperationException("Unable to create GRContext from Metal device."); _device = device; if (device.TryGetFeature() is { } externalObjects) diff --git a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs index ed29231adf..af957551e3 100644 --- a/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/OpenGl/GlSkiaGpu.cs @@ -21,7 +21,7 @@ namespace Avalonia.Skia private bool? _canCreateSurfaces; private readonly IExternalObjectsRenderInterfaceContextFeature? _externalObjectsFeature; - public GlSkiaGpu(IGlContext context, long? maxResourceBytes) + public GlSkiaGpu(IGlContext context, long? maxResourceBytes, bool? useStencilBuffers) { _glContext = context; using (_glContext.EnsureCurrent()) @@ -40,7 +40,8 @@ namespace Avalonia.Skia using(iface) { - _grContext = GRContext.CreateGl(iface, new GRContextOptions { AvoidStencilBuffers = true }); + var avoidStencilBuffers = useStencilBuffers == false; + _grContext = GRContext.CreateGl(iface, new GRContextOptions { AvoidStencilBuffers = avoidStencilBuffers }); if (maxResourceBytes.HasValue) { _grContext.SetResourceCacheLimit(maxResourceBytes.Value); diff --git a/src/Skia/Avalonia.Skia/Gpu/Vulkan/VulkanSkiaGpu.cs b/src/Skia/Avalonia.Skia/Gpu/Vulkan/VulkanSkiaGpu.cs index 476cb31d95..54dfd68fc0 100644 --- a/src/Skia/Avalonia.Skia/Gpu/Vulkan/VulkanSkiaGpu.cs +++ b/src/Skia/Avalonia.Skia/Gpu/Vulkan/VulkanSkiaGpu.cs @@ -13,7 +13,7 @@ internal class VulkanSkiaGpu : ISkiaGpu public IVulkanPlatformGraphicsContext Vulkan { get; private set; } public GRContext GrContext { get; private set; } - public VulkanSkiaGpu(IVulkanPlatformGraphicsContext vulkan, long? maxResourceBytes) + public VulkanSkiaGpu(IVulkanPlatformGraphicsContext vulkan, long? maxResourceBytes, bool? useStencilBuffers) { Vulkan = vulkan; var device = vulkan.Device; @@ -48,7 +48,9 @@ internal class VulkanSkiaGpu : ISkiaGpu GetProcedureAddress = GetProcAddressWrapper }; - GrContext = GRContext.CreateVulkan(ctx) ?? + var avoidStencilBuffers = useStencilBuffers == false; + + GrContext = GRContext.CreateVulkan(ctx, new GRContextOptions { AvoidStencilBuffers = avoidStencilBuffers }) ?? throw new VulkanException("Unable to create GrContext from IVulkanDevice"); if (maxResourceBytes.HasValue) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index df7f6cd48f..0376f1844b 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -20,10 +20,12 @@ namespace Avalonia.Skia internal class PlatformRenderInterface : IPlatformRenderInterface { private readonly long? _maxResourceBytes; + private readonly bool? _useStencilBuffers; - public PlatformRenderInterface(long? maxResourceBytes = null) + public PlatformRenderInterface(long? maxResourceBytes = null, bool? useStencilBuffers = null) { _maxResourceBytes = maxResourceBytes; + _useStencilBuffers = useStencilBuffers; DefaultPixelFormat = SKImageInfo.PlatformColorType.ToPixelFormat(); } @@ -35,11 +37,11 @@ namespace Avalonia.Skia if (graphicsContext is ISkiaGpu skiaGpu) return new SkiaContext(skiaGpu); if (graphicsContext is IGlContext gl) - return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes)); + return new SkiaContext(new GlSkiaGpu(gl, _maxResourceBytes, _useStencilBuffers)); if (graphicsContext is IMetalDevice metal) - return new SkiaContext(new SkiaMetalGpu(metal, _maxResourceBytes)); + return new SkiaContext(new SkiaMetalGpu(metal, _maxResourceBytes, _useStencilBuffers)); if (graphicsContext is IVulkanPlatformGraphicsContext vulkanContext) - return new SkiaContext(new VulkanSkiaGpu(vulkanContext, _maxResourceBytes)); + return new SkiaContext(new VulkanSkiaGpu(vulkanContext, _maxResourceBytes, _useStencilBuffers)); throw new ArgumentException("Graphics context of type is not supported"); } diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs index 84ad547d6c..1a95919300 100644 --- a/src/Skia/Avalonia.Skia/SkiaOptions.cs +++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs @@ -24,5 +24,11 @@ namespace Avalonia /// Enabling this might have performance implications. /// public bool UseOpacitySaveLayer { get; set; } = false; + + /// + /// Gets whether stencil buffers can be used for various draw operations, improving performance. + /// If null (the default), Avalonia chooses whether to enable stencil buffers depending on the platform. + /// + public bool? UseStencilBuffers { get; set; } } } diff --git a/src/Skia/Avalonia.Skia/SkiaPlatform.cs b/src/Skia/Avalonia.Skia/SkiaPlatform.cs index a81b6d5413..c1415147e4 100644 --- a/src/Skia/Avalonia.Skia/SkiaPlatform.cs +++ b/src/Skia/Avalonia.Skia/SkiaPlatform.cs @@ -17,7 +17,7 @@ namespace Avalonia.Skia public static void Initialize(SkiaOptions options) { - var renderInterface = new PlatformRenderInterface(options.MaxGpuResourceSizeBytes); + var renderInterface = new PlatformRenderInterface(options.MaxGpuResourceSizeBytes, options.UseStencilBuffers); AvaloniaLocator.CurrentMutable .Bind().ToConstant(renderInterface)