// Copyright 2019-2021, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Vulkan code for compositors. * * @author Jakob Bornecrantz <jakob@collabora.com> * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> * @author Ryan Pavlik <ryan.pavlik@collabora.com> * @ingroup comp_util */ #include "os/os_time.h" #include "util/u_handles.h" #include "util/u_trace_marker.h" #include "util/comp_vulkan.h" /* * * Helper functions. * */ #define VK_ERROR_RET(VK, FUNC, MSG, RET) VK_ERROR(VK, FUNC ": %s\n\t" MSG, vk_result_string(RET)) static bool get_device_uuid(struct vk_bundle *vk, int gpu_index, uint8_t *uuid) { VkPhysicalDeviceIDProperties pdidp = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, }; VkPhysicalDeviceProperties2 pdp2 = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .pNext = &pdidp, }; VkPhysicalDevice phys[16]; uint32_t gpu_count = ARRAY_SIZE(phys); VkResult ret; ret = vk->vkEnumeratePhysicalDevices(vk->instance, &gpu_count, phys); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vkEnumeratePhysicalDevices", "Failed to enumerate physical devices.", ret); return false; } vk->vkGetPhysicalDeviceProperties2(phys[gpu_index], &pdp2); memcpy(uuid, pdidp.deviceUUID, XRT_GPU_UUID_SIZE); return true; } VkResult fill_in_results(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args, struct comp_vulkan_results *vk_res) { // Grab the device index from the vk_bundle vk_res->selected_gpu_index = vk->physical_device_index; // Grab the suggested device index for the client to use vk_res->client_gpu_index = vk_args->client_gpu_index; // Store physical device UUID for compositor in settings if (vk_res->selected_gpu_index >= 0) { if (get_device_uuid(vk, vk_res->selected_gpu_index, vk_res->selected_gpu_deviceUUID)) { char uuid_str[XRT_GPU_UUID_SIZE * 3 + 1] = {0}; for (int i = 0; i < XRT_GPU_UUID_SIZE; i++) { sprintf(uuid_str + i * 3, "%02x ", vk_res->selected_gpu_deviceUUID[i]); } VK_DEBUG(vk, "Selected %d with uuid: %s", vk_res->selected_gpu_index, uuid_str); } else { VK_ERROR(vk, "Failed to get device %d uuid", vk_res->selected_gpu_index); } } // By default suggest GPU used by compositor to clients if (vk_res->client_gpu_index < 0) { vk_res->client_gpu_index = vk_res->selected_gpu_index; } // Store physical device UUID suggested to clients in settings if (vk_res->client_gpu_index >= 0) { if (get_device_uuid(vk, vk_res->client_gpu_index, vk_res->client_gpu_deviceUUID)) { char uuid_str[XRT_GPU_UUID_SIZE * 3 + 1] = {0}; for (int i = 0; i < XRT_GPU_UUID_SIZE; i++) { sprintf(uuid_str + i * 3, "%02x ", vk_res->client_gpu_deviceUUID[i]); } // Trailing space above, means 'to' should be right next to '%s'. VK_DEBUG(vk, "Suggest %d with uuid: %sto clients", vk_res->client_gpu_index, uuid_str); } else { VK_ERROR(vk, "Failed to get device %d uuid", vk_res->client_gpu_index); } } return VK_SUCCESS; } /* * * Creation functions. * */ static VkResult create_instance(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args) { VkResult ret; VkApplicationInfo app_info = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .pApplicationName = "Monado Compositor", .pEngineName = "Monado", .apiVersion = VK_MAKE_VERSION(1, 0, 2), }; VkInstanceCreateInfo instance_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &app_info, .enabledExtensionCount = u_string_list_get_size(vk_args->instance_extensions), .ppEnabledExtensionNames = u_string_list_get_data(vk_args->instance_extensions), }; ret = vk->vkCreateInstance(&instance_info, NULL, &vk->instance); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vkCreateInstance", "Failed to create Vulkan instance", ret); return ret; } ret = vk_get_instance_functions(vk); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vk_get_instance_functions", "Failed to get Vulkan instance functions.", ret); return ret; } return ret; } static VkResult create_device(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args) { VkResult ret; const char *prio_strs[3] = { "realtime", "high", "normal", }; VkQueueGlobalPriorityEXT prios[3] = { VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, // This is the one we really want. VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT, // Probably not as good but something. VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // Default fallback. }; const bool only_compute_queue = vk_args->only_compute_queue; struct vk_device_features device_features = { .shader_storage_image_write_without_format = true, .null_descriptor = only_compute_queue, .timeline_semaphore = vk_args->timeline_semaphore, }; // No other way then to try to see if realtime is available. for (size_t i = 0; i < ARRAY_SIZE(prios); i++) { ret = vk_create_device( // vk, // vk_args->selected_gpu_index, // only_compute_queue, // compute_only prios[i], // global_priority vk_args->required_device_extensions, // vk_args->optional_device_extensions, // &device_features); // optional_device_features // All ok! if (ret == VK_SUCCESS) { VK_INFO(vk, "Created device and %s queue with %s priority.", only_compute_queue ? "compute" : "graphics", prio_strs[i]); break; } // Try a lower priority. if (ret == VK_ERROR_NOT_PERMITTED_EXT) { continue; } // Some other error! VK_ERROR_RET(vk, "vk_create_device", "Failed to create Vulkan device.", ret); return ret; } ret = vk_init_mutex(vk); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vk_init_mutex", "Failed to init mutex.", ret); return ret; } ret = vk_init_cmd_pool(vk); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vk_init_cmd_pool", "Failed to init command pool.", ret); return ret; } return VK_SUCCESS; } /* * * 'Exported' function. * */ bool comp_vulkan_init_bundle(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args, struct comp_vulkan_results *vk_res) { VkResult ret; vk->log_level = vk_args->log_level; ret = vk_get_loader_functions(vk, vk_args->get_instance_proc_address); if (ret != VK_SUCCESS) { VK_ERROR_RET(vk, "vk_get_loader_functions", "Failed to get VkInstance get process address.", ret); return false; } ret = create_instance(vk, vk_args); if (ret != VK_SUCCESS) { // Error already reported. return false; } ret = create_device(vk, vk_args); if (ret != VK_SUCCESS) { // Error already reported. return false; } ret = fill_in_results(vk, vk_args, vk_res); if (ret != VK_SUCCESS) { // Error already reported. return false; } return true; }