diff --git a/src/Android/Avalonia.Android/AndroidPlatform.cs b/src/Android/Avalonia.Android/AndroidPlatform.cs
index bf9237deece..d9f34198508 100644
--- a/src/Android/Avalonia.Android/AndroidPlatform.cs
+++ b/src/Android/Avalonia.Android/AndroidPlatform.cs
@@ -4,6 +4,7 @@
using Avalonia.Android;
using Avalonia.Android.Platform;
using Avalonia.Android.Platform.Input;
+using Avalonia.Android.Platform.Vulkan;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Input.Platform;
@@ -11,6 +12,7 @@
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
+using Avalonia.Vulkan;
namespace Avalonia
{
@@ -38,7 +40,12 @@ public enum AndroidRenderingMode
///
/// Enables android EGL rendering.
///
- Egl = 2
+ Egl = 2,
+
+ ///
+ /// Enables Vulkan rendering
+ ///
+ Vulkan = 3
}
public sealed class AndroidPlatformOptions
@@ -114,6 +121,13 @@ public static void Initialize()
return egl;
}
}
+
+ if (renderingMode == AndroidRenderingMode.Vulkan)
+ {
+ var vulkan = VulkanSupport.TryInitialize(AvaloniaLocator.Current.GetService() ?? new());
+ if (vulkan != null)
+ return vulkan;
+ }
}
throw new InvalidOperationException($"{nameof(AndroidPlatformOptions)}.{nameof(AndroidPlatformOptions.RenderingMode)} has a value of \"{string.Join(", ", opts.RenderingMode)}\", but no options were applied.");
diff --git a/src/Android/Avalonia.Android/Avalonia.Android.csproj b/src/Android/Avalonia.Android/Avalonia.Android.csproj
index b0b07124801..170cc088fbd 100644
--- a/src/Android/Avalonia.Android/Avalonia.Android.csproj
+++ b/src/Android/Avalonia.Android/Avalonia.Android.csproj
@@ -17,4 +17,8 @@
+
+
+
+
diff --git a/src/Android/Avalonia.Android/Platform/Vulkan/VulkanNativeInterop.cs b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanNativeInterop.cs
new file mode 100644
index 00000000000..d5038fa4d67
--- /dev/null
+++ b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanNativeInterop.cs
@@ -0,0 +1,25 @@
+using System;
+using Avalonia.SourceGenerator;
+using Avalonia.Vulkan;
+
+namespace Avalonia.Android.Platform.Vulkan;
+partial class AndroidVulkanInterface
+{
+ public AndroidVulkanInterface(IVulkanInstance instance)
+ {
+ Initialize(name => instance.GetInstanceProcAddress(instance.Handle, name));
+ }
+
+ [GetProcAddress("vkCreateAndroidSurfaceKHR")]
+ public partial int vkCreateAndroidSurfaceKHR(IntPtr instance, ref VkAndroidSurfaceCreateInfoKHR pCreateInfo,
+ IntPtr pAllocator, out ulong pSurface);
+}
+
+struct VkAndroidSurfaceCreateInfoKHR
+{
+ public const uint VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000;
+ public uint sType;
+ public IntPtr pNext;
+ public uint flags;
+ public IntPtr window;
+}
diff --git a/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs
new file mode 100644
index 00000000000..86f9f5938e3
--- /dev/null
+++ b/src/Android/Avalonia.Android/Platform/Vulkan/VulkanSupport.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Avalonia.Platform;
+using Avalonia.Vulkan;
+
+namespace Avalonia.Android.Platform.Vulkan
+{
+ internal class VulkanSupport
+ {
+ [DllImport("libvulkan.so")]
+ private static extern IntPtr vkGetInstanceProcAddr(IntPtr instance, string name);
+
+ public static VulkanPlatformGraphics? TryInitialize(VulkanOptions options) =>
+ VulkanPlatformGraphics.TryCreate(options ?? new(), new VulkanPlatformSpecificOptions
+ {
+ RequiredInstanceExtensions = { "VK_KHR_android_surface" },
+ GetProcAddressDelegate = vkGetInstanceProcAddr,
+ PlatformFeatures = new Dictionary
+ {
+ [typeof(IVulkanKhrSurfacePlatformSurfaceFactory)] = new VulkanSurfaceFactory()
+ }
+ });
+
+ internal class VulkanSurfaceFactory : IVulkanKhrSurfacePlatformSurfaceFactory
+ {
+ public bool CanRenderToSurface(IVulkanPlatformGraphicsContext context, object surface) =>
+ surface is INativePlatformHandleSurface handle;
+
+ public IVulkanKhrSurfacePlatformSurface CreateSurface(IVulkanPlatformGraphicsContext context, object handle) =>
+ new AndroidVulkanSurface((INativePlatformHandleSurface)handle);
+ }
+
+ class AndroidVulkanSurface : IVulkanKhrSurfacePlatformSurface
+ {
+ private INativePlatformHandleSurface _handle;
+
+ public AndroidVulkanSurface(INativePlatformHandleSurface handle)
+ {
+ _handle = handle;
+ }
+
+ public double Scaling => _handle.Scaling;
+ public PixelSize Size => _handle.Size;
+ public ulong CreateSurface(IVulkanPlatformGraphicsContext context) =>
+ CreateAndroidSurface(_handle.Handle, context.Instance);
+
+ public void Dispose()
+ {
+ // No-op
+ }
+ }
+
+ private static ulong CreateAndroidSurface(nint handle, IVulkanInstance instance)
+ {
+ var vulkanAndroid = new AndroidVulkanInterface(instance);
+ var createInfo = new VkAndroidSurfaceCreateInfoKHR()
+ {
+
+ sType = VkAndroidSurfaceCreateInfoKHR.VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
+ window = handle
+ };
+ VulkanException.ThrowOnError("vkCreateAndroidSurfaceKHR",
+ vulkanAndroid.vkCreateAndroidSurfaceKHR(instance.Handle, ref createInfo, IntPtr.Zero, out var surface));
+ return surface;
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
index b66ea893a3c..7230bc1f24a 100644
--- a/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
+++ b/src/Windows/Avalonia.Win32/Win32PlatformOptions.cs
@@ -23,6 +23,7 @@ public enum Win32RenderingMode
/// Avalonia would try to use native Widows OpenGL with GPU rendering.
///
Wgl = 3,
+
///
/// Avalonia would try to use native Widows Vulkan with GPU rendering.
///