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. ///