The open source OpenXR runtime
at main 452 lines 13 kB view raw
1// Copyright 2019-2023, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Vulkan code for compositors. 6 * 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 9 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 10 * @ingroup comp_util 11 */ 12 13#include "os/os_time.h" 14 15#include "util/u_handles.h" 16#include "util/u_trace_marker.h" 17 18#include "util/comp_vulkan.h" 19 20 21/* 22 * 23 * Helper functions. 24 * 25 */ 26 27#define VK_ERROR_RET(VK, FUNC, MSG, RET) VK_ERROR(VK, FUNC ": %s\n\t" MSG, vk_result_string(RET)) 28 29#define UUID_STR_SIZE (XRT_UUID_SIZE * 3 + 1) 30 31static void 32snprint_luid(char *str, size_t size, xrt_luid_t *luid) 33{ 34 for (size_t i = 0, offset = 0; i < ARRAY_SIZE(luid->data) && offset < size; i++, offset += 3) { 35 snprintf(str + offset, size - offset, "%02x ", luid->data[i]); 36 } 37} 38 39static void 40snprint_uuid(char *str, size_t size, xrt_uuid_t *uuid) 41{ 42 for (size_t i = 0, offset = 0; i < ARRAY_SIZE(uuid->data) && offset < size; i++, offset += 3) { 43 snprintf(str + offset, size - offset, "%02x ", uuid->data[i]); 44 } 45} 46 47static bool 48get_device_id_props(struct vk_bundle *vk, int gpu_index, VkPhysicalDeviceIDProperties *out_id_props) 49{ 50 VkPhysicalDeviceIDProperties pdidp = { 51 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, 52 }; 53 54 VkPhysicalDeviceProperties2 pdp2 = { 55 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, 56 .pNext = &pdidp, 57 }; 58 59 VkPhysicalDevice *phys = NULL; 60 uint32_t gpu_count = 0; 61 VkResult ret; 62 63 ret = vk_enumerate_physical_devices( // 64 vk, // vk_bundle 65 &gpu_count, // out_physical_device_count 66 &phys); // out_physical_devices 67 if (ret != VK_SUCCESS) { 68 VK_ERROR_RET(vk, "vk_enumerate_physical_devices", "Failed to enumerate physical devices.", ret); 69 return false; 70 } 71 if (gpu_count == 0) { 72 VK_ERROR(vk, "vk_enumerate_physical_devices: Returned zero physical devices!"); 73 return false; 74 } 75 76 vk->vkGetPhysicalDeviceProperties2(phys[gpu_index], &pdp2); 77 free(phys); 78 79 *out_id_props = pdidp; 80 81 return true; 82} 83 84static bool 85get_device_uuid(struct vk_bundle *vk, int gpu_index, xrt_uuid_t *uuid) 86{ 87 VkPhysicalDeviceIDProperties pdidp = { 88 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, 89 }; 90 91 if (!get_device_id_props(vk, gpu_index, &pdidp)) { 92 VK_ERROR(vk, "get_device_id_props: false"); 93 return false; 94 } 95 96 memcpy(uuid->data, pdidp.deviceUUID, ARRAY_SIZE(uuid->data)); 97 98 return true; 99} 100 101static bool 102get_device_luid(struct vk_bundle *vk, int gpu_index, xrt_luid_t *luid) 103{ 104 VkPhysicalDeviceIDProperties pdidp = { 105 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, 106 }; 107 108 if (!get_device_id_props(vk, gpu_index, &pdidp)) { 109 VK_ERROR(vk, "get_device_id_props: false"); 110 return false; 111 } 112 113 // Is the LUID even valid? 114 if (pdidp.deviceLUIDValid != VK_TRUE) { 115 return false; 116 } 117 118 memcpy(luid->data, pdidp.deviceLUID, ARRAY_SIZE(luid->data)); 119 120 return true; 121} 122 123VkResult 124fill_in_results(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args, struct comp_vulkan_results *vk_res) 125{ 126 // Grab the device index from the vk_bundle 127 vk_res->selected_gpu_index = vk->physical_device_index; 128 129 // Grab the suggested device index for the client to use 130 vk_res->client_gpu_index = vk_args->client_gpu_index; 131 132 // Store physical device UUID for compositor in settings 133 if (vk_res->selected_gpu_index >= 0) { 134 if (get_device_uuid(vk, vk_res->selected_gpu_index, &vk_res->selected_gpu_deviceUUID)) { 135 char uuid_str[UUID_STR_SIZE] = {0}; 136 snprint_uuid(uuid_str, ARRAY_SIZE(uuid_str), &vk_res->selected_gpu_deviceUUID); 137 138 VK_DEBUG(vk, "Selected %d with uuid: %s", vk_res->selected_gpu_index, uuid_str); 139 } else { 140 VK_ERROR(vk, "Failed to get device %d uuid", vk_res->selected_gpu_index); 141 } 142 } 143 144 // By default suggest GPU used by compositor to clients 145 if (vk_res->client_gpu_index < 0) { 146 vk_res->client_gpu_index = vk_res->selected_gpu_index; 147 } 148 149 // Store physical device UUID suggested to clients in settings 150 if (vk_res->client_gpu_index >= 0) { 151 if (get_device_uuid(vk, vk_res->client_gpu_index, &vk_res->client_gpu_deviceUUID)) { 152 char buffer[UUID_STR_SIZE] = {0}; 153 snprint_uuid(buffer, ARRAY_SIZE(buffer), &vk_res->client_gpu_deviceUUID); 154 155 // Trailing space from snprint_uuid, means 'to' should be right next to '%s'. 156 VK_DEBUG(vk, "Suggest %d with uuid: %sto clients", vk_res->client_gpu_index, buffer); 157 158 if (get_device_luid(vk, vk_res->client_gpu_index, &vk_res->client_gpu_deviceLUID)) { 159 vk_res->client_gpu_deviceLUID_valid = true; 160 snprint_luid(buffer, ARRAY_SIZE(buffer), &vk_res->client_gpu_deviceLUID); 161 VK_DEBUG(vk, "\tDevice LUID: %s", buffer); 162 } 163 } else { 164 VK_ERROR(vk, "Failed to get device %d uuid", vk_res->client_gpu_index); 165 } 166 } 167 168 return VK_SUCCESS; 169} 170 171/* 172 * 173 * Creation functions. 174 * 175 */ 176 177static VkResult 178create_instance(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args) 179{ 180 struct u_string_list *instance_ext_list = NULL; 181 VkResult ret; 182 183 assert(vk_args->required_instance_version != 0); 184 185 186 /* 187 * Extension handling. 188 */ 189 190 // Check required extensions, results in clearer error message. 191 ret = vk_check_required_instance_extensions(vk, vk_args->required_instance_extensions); 192 if (ret == VK_ERROR_EXTENSION_NOT_PRESENT) { 193 return ret; // Already printed. 194 } 195 if (ret != VK_SUCCESS) { 196 VK_ERROR_RET(vk, "vk_check_required_instance_extensions", "Failed to check required extension(s)", ret); 197 return ret; 198 } 199 200 // Build extension list. 201 instance_ext_list = vk_build_instance_extensions( // 202 vk, // 203 vk_args->required_instance_extensions, // 204 vk_args->optional_instance_extensions); // 205 if (!instance_ext_list) { 206 VK_ERROR(vk, "vk_build_instance_extensions: Failed to be list"); 207 return VK_ERROR_EXTENSION_NOT_PRESENT; 208 } 209 210 211 /* 212 * Direct arguments. 213 */ 214 215 VkApplicationInfo app_info = { 216 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 217 .pApplicationName = "Monado Compositor", 218 .pEngineName = "Monado", 219 .apiVersion = vk_args->required_instance_version, 220 }; 221 222 VkInstanceCreateInfo instance_info = { 223 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 224 .pApplicationInfo = &app_info, 225 .enabledExtensionCount = u_string_list_get_size(instance_ext_list), 226 .ppEnabledExtensionNames = u_string_list_get_data(instance_ext_list), 227 }; 228 229 ret = vk->vkCreateInstance(&instance_info, NULL, &vk->instance); 230 if (ret != VK_SUCCESS) { 231 VK_ERROR_RET(vk, "vkCreateInstance", "Failed to create Vulkan instance", ret); 232 return ret; 233 } 234 235 VK_NAME_INSTANCE(vk, vk->instance, "monado vulkan instance"); 236 237 /* 238 * Post creation setup of Vulkan bundle. 239 */ 240 241 // Set information about instance after it has been created. 242 vk->version = vk_args->required_instance_version; 243 244 // Needs to be filled in before getting functions. 245 vk_fill_in_has_instance_extensions(vk, instance_ext_list); 246 247 u_string_list_destroy(&instance_ext_list); 248 249 ret = vk_get_instance_functions(vk); 250 if (ret != VK_SUCCESS) { 251 VK_ERROR_RET(vk, "vk_get_instance_functions", "Failed to get Vulkan instance functions.", ret); 252 return ret; 253 } 254 255 return ret; 256} 257 258static VkResult 259create_device(struct vk_bundle *vk, const struct comp_vulkan_arguments *vk_args) 260{ 261 VkResult ret; 262 263 const char *prio_strs[3] = { 264 "QUEUE_GLOBAL_PRIORITY_REALTIME", 265 "QUEUE_GLOBAL_PRIORITY_HIGH", 266 "QUEUE_GLOBAL_PRIORITY_MEDIUM", 267 }; 268 269 VkQueueGlobalPriorityEXT prios[3] = { 270 VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, // This is the one we really want. 271 VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT, // Probably not as good but something. 272 VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, // Default fallback. 273 }; 274 275 const bool only_compute_queue = vk_args->only_compute_queue; 276 277 struct vk_device_features device_features = { 278 .shader_image_gather_extended = true, 279 .shader_storage_image_write_without_format = true, 280 .storage_buffer_8bit_access = true, 281 .null_descriptor = only_compute_queue, 282 .timeline_semaphore = vk_args->timeline_semaphore, 283 .synchronization_2 = true, 284 .present_wait = true, 285 .video_maintenance_1 = true, 286 }; 287 288 ret = vk_init_mutex(vk); 289 if (ret != VK_SUCCESS) { 290 VK_ERROR_RET(vk, "vk_init_mutex", "Failed to init mutex.", ret); 291 return ret; 292 } 293 294 // No other way then to try to see if realtime is available. 295 for (size_t i = 0; i < ARRAY_SIZE(prios); i++) { 296 ret = vk_create_device( // 297 vk, // 298 vk_args->selected_gpu_index, // 299 only_compute_queue, // compute_only 300 prios[i], // global_priority 301 vk_args->required_device_extensions, // 302 vk_args->optional_device_extensions, // 303 &device_features); // optional_device_features 304 305 // All ok! 306 if (ret == VK_SUCCESS) { 307 VK_INFO(vk, "Created device and %s queue with %s.", only_compute_queue ? "COMPUTE" : "GRAPHICS", 308 prio_strs[i]); 309 break; 310 } 311 312 // Try a lower priority. 313 if (ret == VK_ERROR_NOT_PERMITTED_EXT) { 314 continue; 315 } 316 317 // Some other error! 318 VK_ERROR_RET(vk, "vk_create_device", "Failed to create Vulkan device.", ret); 319 return ret; 320 } 321 322 // All tries failed, return error. Yes this code is clunky. 323 if (ret != VK_SUCCESS) { 324 VK_ERROR_RET(vk, "vk_create_device", "Failed to create Vulkan device.", ret); 325 return ret; 326 } 327 328 // Print device information. 329 vk_print_opened_device_info(vk, U_LOGGING_INFO); 330 331 // Print selected queue(s) information. 332 vk_print_queues_info(vk, U_LOGGING_INFO); 333 334 // Print features enabled. 335 vk_print_features_info(vk, U_LOGGING_INFO); 336 337 // Now that we are done debug some used external handles. 338 vk_print_external_handles_info(vk, U_LOGGING_INFO); 339 340 return VK_SUCCESS; 341} 342 343 344/* 345 * 346 * 'Exported' function. 347 * 348 */ 349 350bool 351comp_vulkan_init_bundle(struct vk_bundle *vk, 352 const struct comp_vulkan_arguments *vk_args, 353 struct comp_vulkan_results *vk_res) 354{ 355 VkResult ret; 356 357 vk->log_level = vk_args->log_level; 358 359 ret = vk_get_loader_functions(vk, vk_args->get_instance_proc_address); 360 if (ret != VK_SUCCESS) { 361 VK_ERROR_RET(vk, "vk_get_loader_functions", "Failed to get VkInstance get process address.", ret); 362 return false; 363 } 364 365 ret = create_instance(vk, vk_args); 366 if (ret != VK_SUCCESS) { 367 // Error already reported. 368 return false; 369 } 370 371 ret = create_device(vk, vk_args); 372 if (ret != VK_SUCCESS) { 373 // Error already reported. 374 return false; 375 } 376 377 ret = fill_in_results(vk, vk_args, vk_res); 378 if (ret != VK_SUCCESS) { 379 // Error already reported. 380 return false; 381 } 382 383 return true; 384} 385 386void 387comp_vulkan_formats_check(struct vk_bundle *vk, struct comp_vulkan_formats *formats) 388{ 389#define CHECK_COLOR(FORMAT) \ 390 formats->has_##FORMAT = vk_csci_is_format_supported(vk, VK_FORMAT_##FORMAT, XRT_SWAPCHAIN_USAGE_COLOR); 391#define CHECK_DS(FORMAT) \ 392 formats->has_##FORMAT = vk_csci_is_format_supported(vk, VK_FORMAT_##FORMAT, XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL); 393 394 VK_CSCI_FORMATS(CHECK_COLOR, CHECK_DS, CHECK_DS, CHECK_DS) 395 396#undef CHECK_COLOR 397#undef CHECK_DS 398 399#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER) 400 /* 401 * Some Vulkan drivers will natively support importing and exporting 402 * SRGB formats (Qualcomm Adreno) even tho technically that's not intended 403 * by the AHardwareBuffer since they don't support sRGB formats. 404 * While others (arm Mali) does not support importing and exporting sRGB 405 * formats. 406 */ 407 if (!formats->has_R8G8B8A8_SRGB && formats->has_R8G8B8A8_UNORM) { 408 formats->has_R8G8B8A8_SRGB = true; 409 formats->emulated_R8G8B8A8_SRGB = true; 410 } 411#endif 412} 413 414void 415comp_vulkan_formats_copy_to_info(const struct comp_vulkan_formats *formats, struct xrt_compositor_info *info) 416{ 417 uint32_t format_count = 0; 418 419#define ADD_IF_SUPPORTED(FORMAT) \ 420 if (formats->has_##FORMAT) { \ 421 info->formats[format_count++] = VK_FORMAT_##FORMAT; \ 422 } 423 424 VK_CSCI_FORMATS(ADD_IF_SUPPORTED, ADD_IF_SUPPORTED, ADD_IF_SUPPORTED, ADD_IF_SUPPORTED) 425 426#undef ADD_IF_SUPPORTED 427 428 assert(format_count <= XRT_MAX_SWAPCHAIN_FORMATS); 429 info->format_count = format_count; 430} 431 432void 433comp_vulkan_formats_log(enum u_logging_level log_level, const struct comp_vulkan_formats *formats) 434{ 435#define PRINT_NAME(FORMAT) "\n\tVK_FORMAT_" #FORMAT ": %s" 436#define PRINT_BOOLEAN(FORMAT) , formats->has_##FORMAT ? "true" : "false" 437 438 U_LOG_IFL_I(log_level, "Supported formats:" // 439 VK_CSCI_FORMATS(PRINT_NAME, PRINT_NAME, PRINT_NAME, PRINT_NAME) // 440 VK_CSCI_FORMATS(PRINT_BOOLEAN, PRINT_BOOLEAN, PRINT_BOOLEAN, PRINT_BOOLEAN) // 441 ); 442 443#undef PRINT_NAME 444#undef PRINT_BOOLEAN 445 446#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER) 447 U_LOG_IFL_I(log_level, 448 "Emulated formats:" 449 "\n\tVK_FORMAT_R8G8B8A8_SRGB: %s", 450 formats->emulated_R8G8B8A8_SRGB ? "emulated" : "native"); 451#endif 452}