Skip to content
Draft
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
38 changes: 34 additions & 4 deletions samples/ControlCatalog.NetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Avalonia.Fonts.Inter;
using Avalonia.Headless;
using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Logging;
using Avalonia.LogicalTree;
using Avalonia.Rendering.Composition;
using Avalonia.Threading;
Expand Down Expand Up @@ -43,6 +44,30 @@ static int Main(string[] args)

var builder = BuildAvaloniaApp();

if (args.FirstOrDefault(x => x.StartsWith("--log-level=")) is { } logLevelArg)
{
var levelStr = logLevelArg.Substring("--log-level=".Length);
if (Enum.TryParse<LogEventLevel>(levelStr, true, out var level))
{
string[] areas = [];
if (args.FirstOrDefault(x => x.StartsWith("--log-areas=")) is { } logAreasArg)
{
var areasStr = logAreasArg.Substring("--log-areas=".Length);
areas = areasStr.Split(',', StringSplitOptions.RemoveEmptyEntries);
}

var l = new object();
builder.LogToDelegate(s =>
{
lock (l)
Console.WriteLine(s);
}, level, areas);
}
}
else
builder.LogToTrace();


double GetScaling()
{
var idx = Array.IndexOf(args, "--scaling");
Expand Down Expand Up @@ -134,13 +159,19 @@ public static AppBuilder BuildAvaloniaApp()
EnableMultiTouch = true,
UseDBusMenu = true,
EnableIme = true,
RenderingMode = new []{X11RenderingMode.OffscreenGbmEgl}
})

.With(new VulkanOptions
{
VulkanInstanceCreationOptions = new ()
VulkanInstanceCreationOptions = new()
{
UseDebug = true
UseDebug = true,
VulkanVersion = new Version(1, 3 )
},
VulkanDeviceCreationOptions =
{
AllowDevicesWithoutKhrSurfaces = true
}
})
.With(new CompositionOptions()
Expand All @@ -163,8 +194,7 @@ public static AppBuilder BuildAvaloniaApp()
: OperatingSystem.IsMacOS() ? new EmbedSampleMac()
: OperatingSystem.IsLinux() ? new EmbedSampleGtk()
: null;
})
.LogToTrace();
});

static void SilenceConsole()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal partial class VulkanSupport
public static VulkanPlatformGraphics? TryInitialize(VulkanOptions options) =>
VulkanPlatformGraphics.TryCreate(options ?? new(), new VulkanPlatformSpecificOptions
{
RequiredInstanceExtensions = { "VK_KHR_android_surface" },
RequiredKhrSurfaceExtensions = { "VK_KHR_android_surface" },
GetProcAddressDelegate = vkGetInstanceProcAddr,
PlatformFeatures = new Dictionary<Type, object>
{
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Vulkan/IVulkanDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,5 @@ public interface IVulkanPlatformGraphicsContext : IPlatformGraphicsContext
internal VkInstance InstanceHandle { get; }
internal VkQueue MainQueueHandle { get; }
internal uint GraphicsQueueFamilyIndex { get; }
IVulkanRenderTarget CreateRenderTarget(IEnumerable<object> surfaces);
IVulkanRenderTarget? CreateRenderTarget(IEnumerable<object> surfaces);
}
51 changes: 44 additions & 7 deletions src/Avalonia.Vulkan/Interop/VulkanDevice.Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Logging;
using Avalonia.Vulkan.UnmanagedInterop;

namespace Avalonia.Vulkan.Interop;
Expand All @@ -24,11 +25,18 @@ public static IVulkanDevice Create(IVulkanInstance instance,
api.EnumeratePhysicalDevices(vkInstance, ref deviceCount, devices)
.ThrowOnError("vkEnumeratePhysicalDevices");

var surfaceForProbePtr = platformOptions.DeviceCheckSurfaceFactory?.Invoke(api.Instance);
var surfaceForProbePtr =
api.IsGetPhysicalDeviceSurfaceSupportKHRAvailable
? platformOptions.DeviceCheckSurfaceFactory?.Invoke(api.Instance)
: null;

var surfaceForProbe = surfaceForProbePtr.HasValue && surfaceForProbePtr.Value != 0
? new VkSurfaceKHR(surfaceForProbePtr.Value)
: (VkSurfaceKHR?)null;

Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Probing {0} physical devices for compatibility with surface {1}", deviceCount, surfaceForProbePtr);

DeviceInfo? compatibleDevice = null, discreteDevice = null;

for (var c = 0; c < deviceCount; c++)
Expand Down Expand Up @@ -66,10 +74,11 @@ public static IVulkanDevice Create(IVulkanInstance instance,
};

var enableExtensions =
new HashSet<string>(options.DeviceExtensions.Concat(VulkanExternalObjectsFeature.RequiredDeviceExtensions));

new HashSet<string>(options.DeviceExtensions.Concat(VulkanExternalObjectsFeature.RequiredDeviceExtensions)
.Append(VK_KHR_swapchain));

var enabledExtensions = enableExtensions
.Intersect(dev.Extensions).Append(VK_KHR_swapchain).Distinct().ToArray();
.Intersect(dev.Extensions).Distinct().ToArray();

using var pEnabledExtensions = new Utf8BufferArray(enabledExtensions);

Expand Down Expand Up @@ -125,11 +134,34 @@ static List<string> GetDeviceExtensions(VulkanInstanceApi instance, VkPhysicalDe
VulkanDeviceCreationOptions options, VkSurfaceKHR? surface)
{
instance.GetPhysicalDeviceProperties(physicalDevice, out var properties);
var deviceName = Marshal.PtrToStringAnsi((IntPtr)properties.deviceName);
Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Found device: {0} (Type: {1}, API Version: {2})",
deviceName,
properties.deviceType,
VulkanUnmanagedHelpers.DecodeVersion(properties.apiVersion));

var supportedExtensions = GetDeviceExtensions(instance, physicalDevice);
if (!supportedExtensions.Contains(VK_KHR_swapchain))
return null;

Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Supported extensions: {0}", string.Join(", ", supportedExtensions));

if (surface != null)
{
var deviceSupportsSwapchain = supportedExtensions.Contains(VK_KHR_swapchain);
if (!deviceSupportsSwapchain)
{
Logger.TryGet(LogEventLevel.Error, "Vulkan")?.Log(null,
"Device {0} does not support VK_KHR_swapchain",
deviceName, VK_KHR_swapchain);
if (!options.AllowDevicesWithoutKhrSurfaces)
return null;
Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Devices without KHR surfaces are allowed by configuration, continuing");
surface = null;
}
}

uint familyCount = 0;
instance.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, ref familyCount, null);
var familyProperties = stackalloc VkQueueFamilyProperties[(int)familyCount];
Expand All @@ -150,6 +182,8 @@ static List<string> GetDeviceExtensions(VulkanInstanceApi instance, VkPhysicalDe
continue;
}

Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Device {0} is compatible", deviceName);
return new DeviceInfo
{
PhysicalDevice = physicalDevice,
Expand All @@ -158,9 +192,12 @@ static List<string> GetDeviceExtensions(VulkanInstanceApi instance, VkPhysicalDe
QueueFamilyIndex = (uint)c,
QueueCount = familyProperties[c].queueCount
};

}

Logger.TryGet(LogEventLevel.Error, "Vulkan")?.Log(null,
"Device {0} does not support {1} or output surface type on any queue family",
deviceName, requredFlags);

return null;
}
}
12 changes: 10 additions & 2 deletions src/Avalonia.Vulkan/Interop/VulkanInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public VulkanInstance(VkInstance handle, VkGetInstanceProcAddressDelegate getPro

internal static unsafe IVulkanInstance Create(
VulkanInstanceCreationOptions options,
VulkanDeviceCreationOptions deviceOptions,
VulkanPlatformSpecificOptions platformOptions)
{
var getProcAddress = options.CustomGetProcAddressDelegate ??
Expand All @@ -48,8 +49,7 @@ internal static unsafe IVulkanInstance Create(
};

var enabledExtensions = new HashSet<string>(options.InstanceExtensions
.Concat(platformOptions.RequiredInstanceExtensions)
.Append("VK_KHR_surface"));
.Concat(platformOptions.RequiredInstanceExtensions));

var enabledLayers = options.EnabledLayers.ToList();

Expand All @@ -68,6 +68,14 @@ void AddExtensionsIfSupported(params string[] names)
}

AddExtensionsIfSupported(VulkanExternalObjectsFeature.RequiredInstanceExtensions);

var khrSurfaceExtensions = platformOptions.RequiredKhrSurfaceExtensions.Append("VK_KHR_surface").ToArray();

if (deviceOptions.AllowDevicesWithoutKhrSurfaces)
AddExtensionsIfSupported(khrSurfaceExtensions);
else
foreach (var ext in khrSurfaceExtensions)
enabledExtensions.Add(ext);

using var enabledExtensionBuffers = new Utf8BufferArray(
enabledExtensions
Expand Down
10 changes: 5 additions & 5 deletions src/Avalonia.Vulkan/UnmanagedInterop/VulkanInstanceApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public partial void GetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice,
public partial VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, byte* pLayerName,
ref uint32_t pPropertyCount, VkExtensionProperties* pProperties);

[GetProcAddress("vkGetPhysicalDeviceSurfaceSupportKHR")]
[GetProcAddress("vkGetPhysicalDeviceSurfaceSupportKHR", true)]
public partial VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
VkSurfaceKHR surface, out VkBool32 pSupported);
Expand All @@ -56,10 +56,10 @@ public partial void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, u
[GetProcAddress("vkGetDeviceProcAddr")]
public partial IntPtr GetDeviceProcAddr(VkDevice device, IntPtr pName);

[GetProcAddress("vkDestroySurfaceKHR")]
[GetProcAddress("vkDestroySurfaceKHR", true)]
public partial void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, IntPtr pAllocator);

[GetProcAddress("vkGetPhysicalDeviceSurfaceFormatsKHR")]
[GetProcAddress("vkGetPhysicalDeviceSurfaceFormatsKHR", true)]
public partial VkResult GetPhysicalDeviceSurfaceFormatsKHR(
VkPhysicalDevice physicalDevice,
VkSurfaceKHR surface,
Expand All @@ -70,11 +70,11 @@ public partial VkResult GetPhysicalDeviceSurfaceFormatsKHR(
public partial void GetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice,
out VkPhysicalDeviceMemoryProperties pMemoryProperties);

[GetProcAddress("vkGetPhysicalDeviceSurfaceCapabilitiesKHR")]
[GetProcAddress("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", true)]
public partial VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
out VkSurfaceCapabilitiesKHR pSurfaceCapabilities);

[GetProcAddress("vkGetPhysicalDeviceSurfacePresentModesKHR")]
[GetProcAddress("vkGetPhysicalDeviceSurfacePresentModesKHR", true)]
public partial VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
ref uint32_t pPresentModeCount, VkPresentModeKHR* pPresentModes);

Expand Down
14 changes: 14 additions & 0 deletions src/Avalonia.Vulkan/UnmanagedInterop/VulkanUnmanagedHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Avalonia.Vulkan.UnmanagedInterop;

static class VulkanUnmanagedHelpers
{
public static Version DecodeVersion(uint bits)
{
var major = (bits >> 22) & 0x7F;
var minor = (bits >> 12) & 0x3FF;
var patch = bits & 0xFFF;
return new Version((int)major, (int)minor, (int)patch);
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Vulkan/VulkanContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void Dispose()

public VulkanInstanceApi InstanceApi { get; }
public VulkanDeviceApi DeviceApi { get; }
public IVulkanRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
public IVulkanRenderTarget? CreateRenderTarget(IEnumerable<object> surfaces)
{
foreach (var surf in surfaces)
{
Expand All @@ -68,6 +68,6 @@ public IVulkanRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
return new VulkanKhrRenderTarget(khrSurface, this);
}

throw new VulkanException("Unable to find a suitable platform surface");
return null;
}
}
9 changes: 9 additions & 0 deletions src/Avalonia.Vulkan/VulkanOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Avalonia.Metadata;

namespace Avalonia.Vulkan;

Expand Down Expand Up @@ -60,11 +61,19 @@ public class VulkanDeviceCreationOptions
public bool PreferDiscreteGpu { get; set; }

public bool RequireComputeBit { get; set; }

/// <summary>
/// Allows selection of devices that lack VK_KHR_surface-related extensions reported by platform backend
/// This might be useful for scenarios where WSI is missing for your display server for whatever reason
/// </summary>
public bool AllowDevicesWithoutKhrSurfaces { get; set; }
}

[Unstable]
public class VulkanPlatformSpecificOptions
{
public IList<string> RequiredInstanceExtensions { get; set; } = new List<string>();
public IList<string> RequiredKhrSurfaceExtensions { get; set; } = new List<string>();
public VkGetInstanceProcAddressDelegate? GetProcAddressDelegate { get; set; }
public Func<IVulkanInstance, ulong>? DeviceCheckSurfaceFactory { get; set; }
public Dictionary<Type, object> PlatformFeatures { get; set; } = new();
Expand Down
5 changes: 5 additions & 0 deletions src/Avalonia.Vulkan/VulkanPlatformGraphics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ public IVulkanDevice CreateDevice(VulkanPlatformSpecificOptions platformOptions)
try
{
instance = VulkanInstance.Create(options.VulkanInstanceCreationOptions ?? new(),
options.VulkanDeviceCreationOptions ?? new(),
platformOptions);

Logger.TryGet(LogEventLevel.Information, "Vulkan")?.Log(null,
"Vulkan instance created. Enabled extensions: {0}",
string.Join(", ", instance.EnabledExtensions));

var devOpts = options.VulkanDeviceCreationOptions ?? new();
Interop.VulkanDevice.Create(instance, devOpts, platformOptions)
.Dispose();
Expand Down
36 changes: 36 additions & 0 deletions src/Avalonia.X11/GbmPlatformGraphics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;

namespace Avalonia.X11;

class GbmPlatformGraphics
{
[DllImport("libEGL.so.1")]
static extern IntPtr eglGetProcAddress(string proc);

[DllImport("libgbm.so.1", SetLastError = true)]
public static extern IntPtr gbm_create_device(int fd);

[DllImport("libc", EntryPoint = "open", SetLastError = true)]
public static extern int open(string pathname, int flags, int mode);

public static IPlatformGraphics? TryCreate(string path)
{
var fd = open(path, 2, 0);
if (fd == -1)
return null;
var gbmDevice = gbm_create_device(fd);
if (gbmDevice == IntPtr.Zero)
return null;
return EglPlatformGraphics.TryCreate(() => new EglDisplay(new EglDisplayCreationOptions
{
Egl = new EglInterface(eglGetProcAddress),
PlatformDisplay = gbmDevice,
PlatformType = 0x31D7,
SupportsMultipleContexts = true,
SupportsContextSharing = true
}));
}
}
Loading
Loading