From 42caf8b38770d9599a3a532a38752466fb236f83 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 4 Aug 2024 23:37:35 +0200 Subject: [PATCH] vulkan: Device initialization This initialized a Vulkan logical device according to compositor preference, but does not yet do anything with it. --- meson.build | 6 +- vulkan.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++ vulkan.h | 20 +++++ wayland.c | 87 +++++++++++++++++++ wayland.h | 6 ++ 5 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 vulkan.c create mode 100644 vulkan.h diff --git a/meson.build b/meson.build index 868d8e36..c18d5461 100644 --- a/meson.build +++ b/meson.build @@ -136,6 +136,8 @@ wayland_cursor = dependency('wayland-cursor') xkb = dependency('xkbcommon', version: '>=1.0.0') fontconfig = dependency('fontconfig') utf8proc = dependency('libutf8proc', required: get_option('grapheme-clustering')) +vulkan = dependency('vulkan') +drm = dependency('libdrm').partial_dependency(compile_args: true, includes: true) if utf8proc.found() add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c') @@ -165,6 +167,7 @@ wl_proto_xml = [ wayland_protocols_datadir / 'unstable/tablet/tablet-unstable-v2.xml', # required by cursor-shape-v1 wayland_protocols_datadir / 'staging/cursor-shape/cursor-shape-v1.xml', wayland_protocols_datadir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml', + wayland_protocols_datadir / 'stable/linux-dmabuf/linux-dmabuf-v1.xml', ] foreach prot : wl_proto_xml @@ -288,6 +291,7 @@ executable( 'search.c', 'search.h', 'server.c', 'server.h', 'client-protocol.h', 'shm.c', 'shm.h', + 'vulkan.c', 'vulkan.h', 'slave.c', 'slave.h', 'spawn.c', 'spawn.h', 'tokenize.c', 'tokenize.h', @@ -297,7 +301,7 @@ executable( 'wayland.c', 'wayland.h', 'shm-formats.h', wl_proto_src + wl_proto_headers, version, dependencies: [math, threads, libepoll, pixman, wayland_client, wayland_cursor, xkb, fontconfig, utf8proc, - tllist, fcft], + tllist, fcft, vulkan, drm], link_with: pgolib, install: true) diff --git a/vulkan.c b/vulkan.c new file mode 100644 index 00000000..4e52ba5f --- /dev/null +++ b/vulkan.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux-dmabuf-v1.h" +#include "vulkan.h" + +#define LOG_MODULE "vulkan" +#define LOG_ENABLE_DBG 1 +#include "log.h" +#include "debug.h" +#include "macros.h" +#include "xmalloc.h" + +static void +log_phdev(const VkPhysicalDeviceProperties *props) +{ +#if LOG_ENABLE_DBG == 1 + uint32_t vv_major = VK_VERSION_MAJOR(props->apiVersion); + uint32_t vv_minor = VK_VERSION_MINOR(props->apiVersion); + uint32_t vv_patch = VK_VERSION_PATCH(props->apiVersion); + + uint32_t dv_major = VK_VERSION_MAJOR(props->driverVersion); + uint32_t dv_minor = VK_VERSION_MINOR(props->driverVersion); + uint32_t dv_patch = VK_VERSION_PATCH(props->driverVersion); + + const char *dev_type = "unknown"; + switch (props->deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + dev_type = "integrated"; + break; + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + dev_type = "discrete"; + break; + case VK_PHYSICAL_DEVICE_TYPE_CPU: + dev_type = "cpu"; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + dev_type = "vgpu"; + break; + default: + break; + } + + LOG_DBG("Vulkan device: %s, type: %s, supported API version: %u.%u.%u, driver version: %u.%u.%u", + props->deviceName, dev_type, vv_major, vv_minor, vv_patch, dv_major, dv_minor, dv_patch); +#endif +} + +static int +vulkan_select_queue_family(struct vulkan *vk, VkPhysicalDevice phdev) +{ + uint32_t qfam_count; + vkGetPhysicalDeviceQueueFamilyProperties(vk->physical_device, &qfam_count, NULL); + assert(qfam_count > 0); + VkQueueFamilyProperties queue_props[qfam_count]; + vkGetPhysicalDeviceQueueFamilyProperties(vk->physical_device, &qfam_count, queue_props); + + for (unsigned i = 0u; i < qfam_count; ++i) { + if (queue_props[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { + return i; + } + } + + for (unsigned i = 0u; i < qfam_count; ++i) { + if (queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + return i; + } + } + + abort(); +} + +static bool +check_extension(const VkExtensionProperties *avail, uint32_t avail_len, const char *name) +{ + for (size_t i = 0; i < avail_len; i++) + if (strcmp(avail[i].extensionName, name) == 0) + return true; + return false; +} + +void +vulkan_destroy(struct vulkan *vk) +{ + if (vk->device) + vkDestroyDevice(vk->device, NULL); + if (vk->instance) + vkDestroyInstance(vk->instance, NULL); + free(vk); +} + +struct vulkan * +vulkan_create(dev_t preferred_device) +{ + LOG_DBG("Creating vulkan backend"); + struct vulkan *vk = calloc(1, sizeof(*vk)); + if (!vk) { + return NULL; + } + + VkApplicationInfo appInfo = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pEngineName = "foot", + .engineVersion = VK_MAKE_VERSION(1, 0, 0), + .apiVersion = VK_API_VERSION_1_1, + }; + + VkInstanceCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &appInfo, + }; + + vk->instance = VK_NULL_HANDLE; + if (vkCreateInstance(&createInfo, NULL, &vk->instance) != VK_SUCCESS) { + LOG_ERR("Could not create Vulkan instance"); + goto error; + } + LOG_DBG("Created instance"); + + // + // Enumerate and pick a physical device. The enumeration can take + // a little while at least on my machines + // + vkEnumeratePhysicalDevices(vk->instance, &vk->device_len, NULL); + if (vk->device_len == 0) { + LOG_ERR("No physical Vulkan devices"); + goto error; + } + + vk->devices = calloc(vk->device_len+1, sizeof(*vk->devices)); + + vkEnumeratePhysicalDevices(vk->instance, &vk->device_len, vk->devices); + LOG_DBG("Enumerated physical Vulkan devices"); + + int chosen = 0; + for (uint32_t idx = 0; idx < vk->device_len; idx++) { + VkPhysicalDevice phdev = vk->devices[idx]; + + uint32_t avail_extc = 0; + if (vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, NULL) != VK_SUCCESS || avail_extc == 0) { + LOG_ERR("Could not enumerate device extensions"); + continue; + } + + VkExtensionProperties avail_ext_props[avail_extc + 1]; + if (vkEnumerateDeviceExtensionProperties(phdev, NULL, &avail_extc, avail_ext_props) != VK_SUCCESS) { + LOG_ERR("Could not enumerate device extensions"); + continue; + } + + bool has_drm_props = check_extension(avail_ext_props, avail_extc, + VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME); + if (!has_drm_props) { + LOG_ERR("Device does not support DRM extension"); + continue; + } + + VkPhysicalDeviceDrmPropertiesEXT drm_props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT, + }; + VkPhysicalDeviceProperties2 props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &drm_props, + }; + vkGetPhysicalDeviceProperties2(phdev, &props); + + log_phdev(&props.properties); + + if (preferred_device == 0) { + // Integrated GPUs are usually better for memory mapping + if (props.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) { + LOG_DBG("Selected integrated GPU"); + chosen = idx; + } + continue; + } + + dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); + dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); + if (primary_devid == preferred_device || render_devid == preferred_device) { + LOG_DBG("Selected preferred physical Vulkan device"); + chosen = idx; + break; + } + } + + vk->physical_device = vk->devices[chosen]; + LOG_DBG("Selected physical Vulkan device"); + + const char *extensions[4] = { 0 }; + size_t extensions_len = 0; + extensions[extensions_len++] = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME; + extensions[extensions_len++] = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME; + extensions[extensions_len++] = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME; + + const float prio = 1.f; + VkDeviceQueueCreateInfo qinfo = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .queueFamilyIndex = vulkan_select_queue_family(vk, vk->physical_device), + .queueCount = 1, + .pQueuePriorities = &prio, + }; + + VkDeviceCreateInfo dev_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .queueCreateInfoCount = 1u, + .pQueueCreateInfos = &qinfo, + .enabledExtensionCount = extensions_len, + .ppEnabledExtensionNames = extensions, + }; + + if (vkCreateDevice(vk->physical_device, &dev_info, NULL, &vk->device) != VK_SUCCESS) { + LOG_ERR("Could not create device"); + goto error; + } + LOG_DBG("Created logical Vulkan device"); + + vk->api.vkGetMemoryFdKHR = + (PFN_vkGetMemoryFdKHR)vkGetDeviceProcAddr(vk->device, "vkGetMemoryFdKHR"); + + return vk; + +error: + vulkan_destroy(vk); + return NULL; +} \ No newline at end of file diff --git a/vulkan.h b/vulkan.h new file mode 100644 index 00000000..e9dd317c --- /dev/null +++ b/vulkan.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +struct vulkan { + VkInstance instance; + VkPhysicalDevice *devices; + uint32_t device_len; + VkPhysicalDevice physical_device; + + VkDevice device; + + struct { + PFN_vkGetMemoryFdKHR vkGetMemoryFdKHR; + } api; +}; + +void vulkan_destroy(struct vulkan *vk); +struct vulkan *vulkan_create(dev_t preferred_device); \ No newline at end of file diff --git a/wayland.c b/wayland.c index 3f65901b..40d2f56e 100644 --- a/wayland.c +++ b/wayland.c @@ -34,6 +34,7 @@ #include "shm-formats.h" #include "util.h" #include "xmalloc.h" +#include "vulkan.h" static void csd_reload_font(struct wl_window *win, float old_scale) @@ -1085,6 +1086,76 @@ fdm_repeat(struct fdm *fdm, int fd, int events, void *data) return true; } +static void +handle_dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) +{ + LOG_DBG("linux dmabuf feedback done"); + struct wayland *wayl = data; + if (wayl->vk == NULL) + wayl->vk = vulkan_create(wayl->preferred_device); +} + +static void +handle_dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) +{ + LOG_DBG("linux dmabuf feedback format table"); + close(fd); +} + +static void +handle_dmabuf_feedback_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *device) +{ + + dev_t dev_id; + assert(device->size == sizeof(dev_id)); + memcpy(&dev_id, device->data, sizeof(dev_id)); + + struct wayland *wayl = data; + wayl->preferred_device = dev_id; + + LOG_DBG("linux dmabuf feedback main device: %ld", dev_id); +} + +static void +handle_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) +{ + LOG_DBG("linux dmabuf feedback tranche done"); +} + +static void +handle_dmabuf_feedback_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *device) +{ + + dev_t dev_id; + assert(device->size == sizeof(dev_id)); + memcpy(&dev_id, device->data, sizeof(dev_id)); + + LOG_DBG("linux dmabuf feedback tranche target device: %ld", dev_id); +} + +static void +handle_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *indices) +{ + LOG_DBG("linux dmabuf feedback tranche formats"); +} + +static void +handle_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) +{ + LOG_DBG("linux dmabuf feedback tranche flags: %d", flags); +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener + linux_dmabuf_feedback_v1_listener = { + .done = handle_dmabuf_feedback_done, + .format_table = handle_dmabuf_feedback_format_table, + .main_device = handle_dmabuf_feedback_main_device, + .tranche_done = handle_dmabuf_feedback_tranche_done, + .tranche_target_device = handle_dmabuf_feedback_tranche_target_device, + .tranche_formats = handle_dmabuf_feedback_tranche_formats, + .tranche_flags = handle_dmabuf_feedback_tranche_flags, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) @@ -1115,6 +1186,18 @@ handle_global(void *data, struct wl_registry *registry, wayl->registry, name, &wl_subcompositor_interface, required); } + else if (streq(interface, zwp_linux_dmabuf_v1_interface.name)) { + const uint32_t required = 4; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->linux_dmabuf = wl_registry_bind( + wayl->registry, name, &zwp_linux_dmabuf_v1_interface, required); + + struct zwp_linux_dmabuf_feedback_v1 *feedback = zwp_linux_dmabuf_v1_get_default_feedback(wayl->linux_dmabuf); + zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &linux_dmabuf_feedback_v1_listener, wayl); + } + else if (streq(interface, wl_shm_interface.name)) { const uint32_t required = 1; if (!verify_iface_version(interface, version, required)) @@ -1679,6 +1762,10 @@ wayl_destroy(struct wayland *wayl) zwp_text_input_manager_v3_destroy(wayl->text_input_manager); #endif + if (wayl->vk != NULL) + vulkan_destroy(wayl->vk); + if (wayl->linux_dmabuf != NULL) + zwp_linux_dmabuf_v1_destroy(wayl->linux_dmabuf); if (wayl->single_pixel_manager != NULL) wp_single_pixel_buffer_manager_v1_destroy(wayl->single_pixel_manager); if (wayl->fractional_scale_manager != NULL) diff --git a/wayland.h b/wayland.h index ca9c05fa..81e11486 100644 --- a/wayland.h +++ b/wayland.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -425,6 +427,7 @@ struct wayland { struct wl_compositor *compositor; struct wl_subcompositor *sub_compositor; struct wl_shm *shm; + struct zwp_linux_dmabuf_v1 *linux_dmabuf; struct zxdg_output_manager_v1 *xdg_output_manager; @@ -458,6 +461,9 @@ struct wayland { /* WL_SHM >= 2 */ bool use_shm_release; + + struct vulkan *vk; + dev_t preferred_device; }; struct wayland *wayl_init(