From 3e242423f0aaa7a8e666e951b813d49fb1f66119 Mon Sep 17 00:00:00 2001 From: Emmanuel Hansen Date: Wed, 8 Jan 2025 20:33:42 +0000 Subject: [PATCH] fix segfault crash when native surface is destroyed while there's rendering queued on vulkan android (#17921) --- .../SkiaPlatform/InvalidationAwareSurfaceView.cs | 6 +++++- .../Platform/Vulkan/VulkanSupport.cs | 2 ++ src/Avalonia.Vulkan/Interop/VulkanDisplay.cs | 12 +++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs index 2ec255df19..91b03a2aa5 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs @@ -14,12 +14,13 @@ namespace Avalonia.Android { bool _invalidateQueued; private bool _isDisposed; + private bool _isSurfaceValid; readonly object _lock = new object(); private readonly Handler _handler; internal event EventHandler? SurfaceWindowCreated; - IntPtr IPlatformHandle.Handle => Holder?.Surface?.Handle is { } handle ? + IntPtr IPlatformHandle.Handle => _isSurfaceValid && Holder?.Surface?.Handle is { } handle ? AndroidFramebuffer.ANativeWindow_fromSurface(JNIEnv.Handle, handle) : default; @@ -63,12 +64,14 @@ namespace Avalonia.Android public void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height) { + _isSurfaceValid = true; Log.Info("AVALONIA", "Surface Changed"); DoDraw(); } public void SurfaceCreated(ISurfaceHolder holder) { + _isSurfaceValid = true; Log.Info("AVALONIA", "Surface Created"); SurfaceWindowCreated?.Invoke(this, EventArgs.Empty); DoDraw(); @@ -76,6 +79,7 @@ namespace Avalonia.Android public void SurfaceDestroyed(ISurfaceHolder holder) { + _isSurfaceValid = false; Log.Info("AVALONIA", "Surface Destroyed"); } diff --git a/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs index 5d1b0e309a..c1abaa05a5 100644 --- a/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs +++ b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs @@ -53,6 +53,8 @@ namespace Avalonia.Android.Platform.Vulkan private static ulong CreateAndroidSurface(nint handle, IVulkanInstance instance) { + if(handle == IntPtr.Zero) + throw new ArgumentException("Surface handle can't be 0x0", nameof(handle)); var vulkanAndroid = new AndroidVulkanInterface(instance); var createInfo = new VkAndroidSurfaceCreateInfoKHR() { diff --git a/src/Avalonia.Vulkan/Interop/VulkanDisplay.cs b/src/Avalonia.Vulkan/Interop/VulkanDisplay.cs index 8f4da16afc..ba767f7c1e 100644 --- a/src/Avalonia.Vulkan/Interop/VulkanDisplay.cs +++ b/src/Avalonia.Vulkan/Interop/VulkanDisplay.cs @@ -13,7 +13,7 @@ internal class VulkanDisplay : IDisposable private IVulkanPlatformGraphicsContext _context; private VulkanSemaphorePair _semaphorePair; private uint _nextImage; - private VulkanKhrSurface _surface; + private VulkanKhrSurface? _surface; private VkSurfaceFormatKHR _surfaceFormat; private VkSwapchainKHR _swapchain; private VkExtent2D _swapchainExtent; @@ -40,7 +40,7 @@ internal class VulkanDisplay : IDisposable { get { - if (_surfaceFormat.format == VkFormat.VK_FORMAT_UNDEFINED) + if (_surfaceFormat.format == VkFormat.VK_FORMAT_UNDEFINED && _surface != null) _surfaceFormat = _surface.GetSurfaceFormat(); return _surfaceFormat; } @@ -193,6 +193,11 @@ internal class VulkanDisplay : IDisposable private void RecreateSwapchain() { + if (_surface == null) + { + RecreateSurface(); + return; + } _context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle); _swapchain = CreateSwapchain(_context, _surface, out var extent, this); _swapchainExtent = extent; @@ -202,6 +207,7 @@ internal class VulkanDisplay : IDisposable private void RecreateSurface() { _surface?.Dispose(); + _surface = null; _surface = new VulkanKhrSurface(_context, _platformSurface); DestroySwapchain(); RecreateSwapchain(); @@ -209,7 +215,7 @@ internal class VulkanDisplay : IDisposable public bool EnsureSwapchainAvailable() { - if (Size != _surface.Size) + if (Size != _surface?.Size) { RecreateSwapchain(); return true;