The open source OpenXR runtime
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}