Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android - Fix segfault crash when native surface is destroyed on vulkan #17921

Merged
merged 1 commit into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ internal abstract class InvalidationAwareSurfaceView : SurfaceView, ISurfaceHold
{
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) :
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaks memory, BTW. ANativeWindow_fromSurface increases the reference counter for returned ANativeWindow object.

It's out of the scope of the current PR but should be addressed at some point

default;

Expand Down Expand Up @@ -63,19 +64,22 @@ public override void Invalidate()

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();
}

public void SurfaceDestroyed(ISurfaceHolder holder)
{
_isSurfaceValid = false;
Log.Info("AVALONIA", "Surface Destroyed");

}
Expand Down
2 changes: 2 additions & 0 deletions src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public void Dispose()

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()
{
Expand Down
12 changes: 9 additions & 3 deletions src/Avalonia.Vulkan/Interop/VulkanDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,7 +40,7 @@ internal VkSurfaceFormatKHR SurfaceFormat
{
get
{
if (_surfaceFormat.format == VkFormat.VK_FORMAT_UNDEFINED)
if (_surfaceFormat.format == VkFormat.VK_FORMAT_UNDEFINED && _surface != null)
_surfaceFormat = _surface.GetSurfaceFormat();
return _surfaceFormat;
}
Expand Down Expand Up @@ -193,6 +193,11 @@ private VkImageView CreateSwapchainImageView(VkImage swapchainImage, VkFormat fo

private void RecreateSwapchain()
{
if (_surface == null)
{
RecreateSurface();
return;
}
_context.DeviceApi.DeviceWaitIdle(_context.DeviceHandle);
_swapchain = CreateSwapchain(_context, _surface, out var extent, this);
_swapchainExtent = extent;
Expand All @@ -202,14 +207,15 @@ private void RecreateSwapchain()
private void RecreateSurface()
{
_surface?.Dispose();
_surface = null;
_surface = new VulkanKhrSurface(_context, _platformSurface);
DestroySwapchain();
RecreateSwapchain();
}

public bool EnsureSwapchainAvailable()
{
if (Size != _surface.Size)
if (Size != _surface?.Size)
{
RecreateSwapchain();
return true;
Expand Down
Loading