The open source OpenXR runtime
at prediction-2 620 lines 20 kB view raw
1// Copyright 2019-2024, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Null compositor implementation. 6 * 7 * Originally based on src/xrt/compositor/main/comp_compositor.c 8 * 9 * @author Jakob Bornecrantz <jakob@collabora.com> 10 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 11 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 12 * @ingroup comp_null 13 */ 14 15#include "null_compositor.h" 16#include "null_interfaces.h" 17 18#include "os/os_time.h" 19 20#include "util/u_misc.h" 21#include "util/u_pacing.h" 22#include "util/u_time.h" 23#include "util/u_debug.h" 24#include "util/u_verify.h" 25#include "util/u_handles.h" 26#include "util/u_trace_marker.h" 27 28#include "util/comp_vulkan.h" 29 30#include "multi/comp_multi_interface.h" 31#include "xrt/xrt_compositor.h" 32#include "xrt/xrt_device.h" 33 34 35#include <stdint.h> 36#include <stdio.h> 37 38static const uint64_t RECOMMENDED_VIEW_WIDTH = 320; 39static const uint64_t RECOMMENDED_VIEW_HEIGHT = 240; 40 41static const uint64_t MAX_VIEW_WIDTH = 1920; 42static const uint64_t MAX_VIEW_HEIGHT = 1080; 43 44DEBUG_GET_ONCE_LOG_OPTION(log, "XRT_COMPOSITOR_LOG", U_LOGGING_INFO) 45 46 47/* 48 * 49 * Helper functions. 50 * 51 */ 52 53static struct vk_bundle * 54get_vk(struct null_compositor *c) 55{ 56 return &c->base.vk; 57} 58 59 60/* 61 * 62 * Vulkan extensions. 63 * 64 */ 65 66static const char *instance_extensions_common[] = { 67 VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME, // 68 VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, // 69 VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, // 70 VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, // 71}; 72 73static const char *required_device_extensions[] = { 74 VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, // 75 VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME, // 76 VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, // 77 VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, // 78 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, // 79 80// Platform version of "external_memory" 81#if defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_FD) 82 VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, 83 84#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_AHARDWAREBUFFER) 85 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, 86 VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, 87 VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 88 VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, 89 VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME, 90 91#elif defined(XRT_GRAPHICS_BUFFER_HANDLE_IS_WIN32_HANDLE) 92 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, 93 94#else 95#error "Need port!" 96#endif 97 98// Platform version of "external_fence" and "external_semaphore" 99#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD) // Optional 100 101#elif defined(XRT_GRAPHICS_SYNC_HANDLE_IS_WIN32_HANDLE) 102 VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, // 103 VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME, // 104 105#else 106#error "Need port!" 107#endif 108}; 109 110static const char *optional_device_extensions[] = { 111 VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, // 112 VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, // 113 114// Platform version of "external_fence" and "external_semaphore" 115#if defined(XRT_GRAPHICS_SYNC_HANDLE_IS_FD) // Optional 116 VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, // 117 VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME, // 118 119#elif defined(XRT_GRAPHICS_SYNC_HANDLE_IS_WIN32_HANDLE) // Not optional 120 121#else 122#error "Need port!" 123#endif 124 125#ifdef VK_KHR_global_priority 126 VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME, 127#endif 128#ifdef VK_KHR_image_format_list 129 VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, 130#endif 131#ifdef VK_KHR_maintenance1 132 VK_KHR_MAINTENANCE_1_EXTENSION_NAME, 133#endif 134#ifdef VK_KHR_maintenance2 135 VK_KHR_MAINTENANCE_2_EXTENSION_NAME, 136#endif 137#ifdef VK_KHR_timeline_semaphore 138 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, 139#endif 140#ifdef VK_EXT_calibrated_timestamps 141 VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, 142#endif 143#ifdef VK_EXT_robustness2 144 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 145#endif 146}; 147 148static VkResult 149select_instances_extensions(struct null_compositor *c, struct u_string_list *required, struct u_string_list *optional) 150{ 151#ifdef VK_EXT_display_surface_counter 152 u_string_list_append(optional, VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME); 153#endif 154 155 return VK_SUCCESS; 156} 157 158static bool 159compositor_init_vulkan(struct null_compositor *c) 160{ 161 struct vk_bundle *vk = get_vk(c); 162 VkResult ret; 163 164 // every backend needs at least the common extensions 165 struct u_string_list *required_instance_ext_list = 166 u_string_list_create_from_array(instance_extensions_common, ARRAY_SIZE(instance_extensions_common)); 167 168 struct u_string_list *optional_instance_ext_list = u_string_list_create(); 169 170 ret = select_instances_extensions(c, required_instance_ext_list, optional_instance_ext_list); 171 if (ret != VK_SUCCESS) { 172 VK_ERROR(vk, "select_instances_extensions: %s\n\tFailed to select instance extensions.", 173 vk_result_string(ret)); 174 u_string_list_destroy(&required_instance_ext_list); 175 u_string_list_destroy(&optional_instance_ext_list); 176 return ret; 177 } 178 179 struct u_string_list *required_device_extension_list = 180 u_string_list_create_from_array(required_device_extensions, ARRAY_SIZE(required_device_extensions)); 181 182 struct u_string_list *optional_device_extension_list = 183 u_string_list_create_from_array(optional_device_extensions, ARRAY_SIZE(optional_device_extensions)); 184 185 struct comp_vulkan_arguments vk_args = { 186 .get_instance_proc_address = vkGetInstanceProcAddr, 187 .required_instance_version = VK_MAKE_VERSION(1, 0, 0), 188 .required_instance_extensions = required_instance_ext_list, 189 .optional_instance_extensions = optional_instance_ext_list, 190 .required_device_extensions = required_device_extension_list, 191 .optional_device_extensions = optional_device_extension_list, 192 .log_level = c->settings.log_level, 193 .only_compute_queue = false, // Regular GFX 194 .selected_gpu_index = -1, // Auto 195 .client_gpu_index = -1, // Auto 196 .timeline_semaphore = true, // Flag is optional, not a hard requirement. 197 }; 198 199 struct comp_vulkan_results vk_res = {0}; 200 bool bundle_ret = comp_vulkan_init_bundle(vk, &vk_args, &vk_res); 201 202 u_string_list_destroy(&required_instance_ext_list); 203 u_string_list_destroy(&optional_instance_ext_list); 204 u_string_list_destroy(&required_device_extension_list); 205 u_string_list_destroy(&optional_device_extension_list); 206 207 if (!bundle_ret) { 208 return false; 209 } 210 211 // clang-format off 212 static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceUUID.data) == XRT_UUID_SIZE, "array size mismatch"); 213 static_assert(ARRAY_SIZE(vk_res.selected_gpu_deviceUUID.data) == XRT_UUID_SIZE, "array size mismatch"); 214 static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceUUID.data) == ARRAY_SIZE(c->sys_info.client_vk_deviceUUID.data), "array size mismatch"); 215 static_assert(ARRAY_SIZE(vk_res.selected_gpu_deviceUUID.data) == ARRAY_SIZE(c->sys_info.compositor_vk_deviceUUID.data), "array size mismatch"); 216 static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceLUID.data) == XRT_LUID_SIZE, "array size mismatch"); 217 static_assert(ARRAY_SIZE(vk_res.client_gpu_deviceLUID.data) == ARRAY_SIZE(c->sys_info.client_d3d_deviceLUID.data), "array size mismatch"); 218 // clang-format on 219 220 c->sys_info.client_vk_deviceUUID = vk_res.client_gpu_deviceUUID; 221 c->sys_info.compositor_vk_deviceUUID = vk_res.selected_gpu_deviceUUID; 222 c->sys_info.client_d3d_deviceLUID = vk_res.client_gpu_deviceLUID; 223 c->sys_info.client_d3d_deviceLUID_valid = vk_res.client_gpu_deviceLUID_valid; 224 225 // Tie the lifetimes of swapchains to Vulkan. 226 xrt_result_t xret = comp_swapchain_shared_init(&c->base.cscs, vk); 227 if (xret != XRT_SUCCESS) { 228 return false; 229 } 230 231 return true; 232} 233 234 235/* 236 * 237 * Other init functions. 238 * 239 */ 240 241static bool 242compositor_init_pacing(struct null_compositor *c) 243{ 244 xrt_result_t xret = u_pc_fake_create(c->settings.frame_interval_ns, os_monotonic_get_ns(), &c->upc); 245 if (xret != XRT_SUCCESS) { 246 NULL_ERROR(c, "Failed to create fake pacing helper!"); 247 return false; 248 } 249 250 return true; 251} 252 253static bool 254compositor_init_info(struct null_compositor *c) 255{ 256 struct xrt_compositor_info *info = &c->base.base.base.info; 257 258 struct comp_vulkan_formats formats = {0}; 259 comp_vulkan_formats_check(get_vk(c), &formats); 260 comp_vulkan_formats_copy_to_info(&formats, info); 261 comp_vulkan_formats_log(c->settings.log_level, &formats); 262 263 return true; 264} 265 266static bool 267compositor_init_sys_info(struct null_compositor *c, struct xrt_device *xdev) 268{ 269 struct xrt_system_compositor_info *sys_info = &c->sys_info; 270 271 // Required by OpenXR spec. 272 sys_info->max_layers = XRT_MAX_LAYERS; 273 274 uint32_t view_count = xdev->hmd->view_count; 275 enum xrt_view_type view_type = 0; // Invalid 276 277 switch (view_count) { 278 case 0: U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be > 0!", xdev->str); return false; 279 case 1: view_type = XRT_VIEW_TYPE_MONO; break; 280 case 2: view_type = XRT_VIEW_TYPE_STEREO; break; 281 default: 282 U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be 1 or 2, not %u!", xdev->str, view_count); 283 return false; 284 } 285 286 // UUIDs and LUID already set in vk init. 287 (void)sys_info->compositor_vk_deviceUUID; 288 (void)sys_info->client_vk_deviceUUID; 289 (void)sys_info->client_d3d_deviceLUID; 290 (void)sys_info->client_d3d_deviceLUID_valid; 291 // clang-format off 292 for (uint32_t i = 0; i < view_count; ++i) { 293 sys_info->view_configs[0].views[i].recommended.width_pixels = RECOMMENDED_VIEW_WIDTH; 294 sys_info->view_configs[0].views[i].recommended.height_pixels = RECOMMENDED_VIEW_HEIGHT; 295 sys_info->view_configs[0].views[i].recommended.sample_count = 1; 296 sys_info->view_configs[0].views[i].max.width_pixels = MAX_VIEW_WIDTH; 297 sys_info->view_configs[0].views[i].max.height_pixels = MAX_VIEW_HEIGHT; 298 sys_info->view_configs[0].views[i].max.sample_count = 1; 299 } 300 // clang-format on 301 sys_info->view_configs[0].view_type = view_type; 302 sys_info->view_configs[0].view_count = view_count; 303 sys_info->view_config_count = 1; // Only one view config type supported. 304 305 // Copy the list directly. 306 assert(xdev->hmd->blend_mode_count <= XRT_MAX_DEVICE_BLEND_MODES); 307 assert(xdev->hmd->blend_mode_count != 0); 308 assert(xdev->hmd->blend_mode_count <= ARRAY_SIZE(sys_info->supported_blend_modes)); 309 for (size_t i = 0; i < xdev->hmd->blend_mode_count; ++i) { 310 assert(u_verify_blend_mode_valid(xdev->hmd->blend_modes[i])); 311 sys_info->supported_blend_modes[i] = xdev->hmd->blend_modes[i]; 312 } 313 sys_info->supported_blend_mode_count = (uint8_t)xdev->hmd->blend_mode_count; 314 315 // Refresh rates. 316 sys_info->refresh_rate_count = 1; 317 sys_info->refresh_rates_hz[0] = (float)(1. / time_ns_to_s(c->settings.frame_interval_ns)); 318 319 return true; 320} 321 322 323/* 324 * 325 * Member functions. 326 * 327 */ 328 329static xrt_result_t 330null_compositor_begin_session(struct xrt_compositor *xc, const struct xrt_begin_session_info *type) 331{ 332 struct null_compositor *c = null_compositor(xc); 333 NULL_DEBUG(c, "BEGIN_SESSION"); 334 335 /* 336 * No logic needed here for the null compositor, if using the null 337 * compositor as a base for a new compositor put desired logic here. 338 */ 339 340 return XRT_SUCCESS; 341} 342 343static xrt_result_t 344null_compositor_end_session(struct xrt_compositor *xc) 345{ 346 struct null_compositor *c = null_compositor(xc); 347 NULL_DEBUG(c, "END_SESSION"); 348 349 /* 350 * No logic needed here for the null compositor, if using the null 351 * compositor as a base for a new compositor put desired logic here. 352 */ 353 354 return XRT_SUCCESS; 355} 356 357static xrt_result_t 358null_compositor_predict_frame(struct xrt_compositor *xc, 359 int64_t *out_frame_id, 360 int64_t *out_wake_time_ns, 361 int64_t *out_predicted_gpu_time_ns, 362 int64_t *out_predicted_display_time_ns, 363 int64_t *out_predicted_display_period_ns) 364{ 365 COMP_TRACE_MARKER(); 366 367 struct null_compositor *c = null_compositor(xc); 368 NULL_TRACE(c, "PREDICT_FRAME"); 369 370 int64_t now_ns = os_monotonic_get_ns(); 371 int64_t null_desired_present_time_ns = 0; 372 int64_t null_present_slop_ns = 0; 373 int64_t null_min_display_period_ns = 0; 374 375 u_pc_predict( // 376 c->upc, // upc 377 now_ns, // now_ns 378 out_frame_id, // out_frame_id 379 out_wake_time_ns, // out_wake_up_time_ns 380 &null_desired_present_time_ns, // out_desired_present_time_ns 381 &null_present_slop_ns, // out_present_slop_ns 382 out_predicted_display_time_ns, // out_predicted_display_time_ns 383 out_predicted_display_period_ns, // out_predicted_display_period_ns 384 &null_min_display_period_ns); // out_min_display_period_ns 385 386 return XRT_SUCCESS; 387} 388 389static xrt_result_t 390null_compositor_mark_frame(struct xrt_compositor *xc, 391 int64_t frame_id, 392 enum xrt_compositor_frame_point point, 393 int64_t when_ns) 394{ 395 COMP_TRACE_MARKER(); 396 397 struct null_compositor *c = null_compositor(xc); 398 NULL_TRACE(c, "MARK_FRAME %i", point); 399 400 switch (point) { 401 case XRT_COMPOSITOR_FRAME_POINT_WOKE: 402 u_pc_mark_point(c->upc, U_TIMING_POINT_WAKE_UP, frame_id, when_ns); 403 return XRT_SUCCESS; 404 default: assert(false); 405 } 406 407 return XRT_SUCCESS; 408} 409 410static xrt_result_t 411null_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id) 412{ 413 struct null_compositor *c = null_compositor(xc); 414 NULL_TRACE(c, "BEGIN_FRAME"); 415 416 /* 417 * No logic needed here for the null compositor, if using the null 418 * compositor as a base for a new compositor put desired logic here. 419 */ 420 421 return XRT_SUCCESS; 422} 423 424static xrt_result_t 425null_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id) 426{ 427 struct null_compositor *c = null_compositor(xc); 428 NULL_TRACE(c, "DISCARD_FRAME"); 429 430 // Shouldn't be called. 431 assert(false); 432 433 return XRT_SUCCESS; 434} 435 436static xrt_result_t 437null_compositor_layer_commit(struct xrt_compositor *xc, xrt_graphics_sync_handle_t sync_handle) 438{ 439 COMP_TRACE_MARKER(); 440 441 struct null_compositor *c = null_compositor(xc); 442 NULL_TRACE(c, "LAYER_COMMIT"); 443 444 int64_t frame_id = c->base.layer_accum.data.frame_id; 445 int64_t display_time_ns = c->base.layer_accum.layers[0].data.timestamp; 446 447 // Default value from monado, overridden by HMD device where possible. 448 struct xrt_vec3 default_eye_relation = {0.063f, 0.f, 0.f}; 449 struct xrt_space_relation head_relation = {0}; 450 451 struct xrt_fov fovs[2] = {0}; 452 struct xrt_pose poses[2] = {0}; 453 454 xrt_result_t xret = xrt_device_get_view_poses( // 455 c->xdev, // 456 &default_eye_relation, // 457 display_time_ns, // 458 XRT_VIEW_TYPE_STEREO, // 459 2, // 460 &head_relation, // 461 fovs, // 462 poses); // 463 if (xret != XRT_SUCCESS) { 464 return xret; 465 } 466 467 468 /* 469 * The null compositor doesn't render any frames, but needs to do 470 * minimal bookkeeping and handling of arguments. If using the null 471 * compositor as a base for a new compositor this is where you render 472 * frames to be displayed to devices or remote clients. 473 */ 474 475 // If you are using the system/multi-compositor (multiple client module), your native compositor 476 // can just unref the sync handle. Otherwise please use it. 477 u_graphics_sync_unref(&sync_handle); 478 479 /* 480 * Time keeping needed to keep the pacer happy. 481 */ 482 483 // When we begin rendering. 484 { 485 int64_t now_ns = os_monotonic_get_ns(); 486 u_pc_mark_point(c->upc, U_TIMING_POINT_BEGIN, frame_id, now_ns); 487 } 488 489 // When we are submitting to the GPU. 490 { 491 int64_t now_ns = os_monotonic_get_ns(); 492 u_pc_mark_point(c->upc, U_TIMING_POINT_SUBMIT_BEGIN, frame_id, now_ns); 493 494 now_ns = os_monotonic_get_ns(); 495 u_pc_mark_point(c->upc, U_TIMING_POINT_SUBMIT_END, frame_id, now_ns); 496 } 497 498 // Now is a good point to garbage collect. 499 comp_swapchain_shared_garbage_collect(&c->base.cscs); 500 501 return XRT_SUCCESS; 502} 503 504static void 505null_compositor_destroy(struct xrt_compositor *xc) 506{ 507 struct null_compositor *c = null_compositor(xc); 508 struct vk_bundle *vk = get_vk(c); 509 510 NULL_DEBUG(c, "NULL_COMP_DESTROY"); 511 512 // Make sure we don't have anything to destroy. 513 comp_swapchain_shared_garbage_collect(&c->base.cscs); 514 515 // Must be destroyed before Vulkan. 516 comp_swapchain_shared_destroy(&c->base.cscs, vk); 517 518 if (vk->device != VK_NULL_HANDLE) { 519 vk->vkDestroyDevice(vk->device, NULL); 520 vk->device = VK_NULL_HANDLE; 521 } 522 523 vk_deinit_mutex(vk); 524 525 if (vk->instance != VK_NULL_HANDLE) { 526 vk->vkDestroyInstance(vk->instance, NULL); 527 vk->instance = VK_NULL_HANDLE; 528 } 529 530 comp_base_fini(&c->base); 531 532 u_pc_destroy(&c->upc); 533 534 free(c); 535} 536 537static xrt_result_t 538null_compositor_get_display_refresh_rate(struct xrt_compositor *xc, float *out_display_refresh_rate_hz) 539{ 540 struct null_compositor *c = null_compositor(xc); 541 542 *out_display_refresh_rate_hz = c->sys_info.refresh_rates_hz[0]; 543 return XRT_SUCCESS; 544} 545 546static xrt_result_t 547null_compositor_request_display_refresh_rate(struct xrt_compositor *xc, float display_refresh_rate_hz) 548{ 549 return XRT_SUCCESS; 550} 551 552/* 553 * 554 * 'Exported' functions. 555 * 556 */ 557 558xrt_result_t 559null_compositor_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc) 560{ 561 struct null_compositor *c = U_TYPED_CALLOC(struct null_compositor); 562 563 struct xrt_compositor *iface = &c->base.base.base; 564 iface->begin_session = null_compositor_begin_session; 565 iface->end_session = null_compositor_end_session; 566 iface->predict_frame = null_compositor_predict_frame; 567 iface->mark_frame = null_compositor_mark_frame; 568 iface->begin_frame = null_compositor_begin_frame; 569 iface->discard_frame = null_compositor_discard_frame; 570 iface->layer_commit = null_compositor_layer_commit; 571 iface->destroy = null_compositor_destroy; 572 c->base.base.base.get_display_refresh_rate = null_compositor_get_display_refresh_rate; 573 c->base.base.base.request_display_refresh_rate = null_compositor_request_display_refresh_rate; 574 c->settings.log_level = debug_get_log_option_log(); 575 c->frame.waited.id = -1; 576 c->frame.rendering.id = -1; 577 c->settings.frame_interval_ns = U_TIME_1S_IN_NS / 20; // 20 FPS 578 c->xdev = xdev; 579 580 NULL_DEBUG(c, "Doing init %p", (void *)c); 581 582 NULL_INFO(c, 583 "\n" 584 "################################################################################\n" 585 "# Null compositor starting, if you intended to use the null compositor (for CI #\n" 586 "# integration) then everything is mostly likely setup correctly. But if you #\n" 587 "# intended to use Monado with real hardware it you probably built Monado #\n" 588 "# without the main compositor, please check your build config and make sure #\n" 589 "# that the main compositor is being built. Also make sure that the environment #\n" 590 "# variable XRT_COMPOSITOR_NULL is not set. #\n" 591 "################################################################################"); 592 593 // Do this as early as possible 594 comp_base_init(&c->base); 595 596 597 /* 598 * Main init sequence. 599 */ 600 601 if (!compositor_init_pacing(c) || // 602 !compositor_init_vulkan(c) || // 603 !compositor_init_sys_info(c, xdev) || // 604 !compositor_init_info(c)) { // 605 NULL_DEBUG(c, "Failed to init compositor %p", (void *)c); 606 c->base.base.base.destroy(&c->base.base.base); 607 608 return XRT_ERROR_VULKAN; 609 } 610 611 612 NULL_DEBUG(c, "Done %p", (void *)c); 613 614 // Standard app pacer. 615 struct u_pacing_app_factory *upaf = NULL; 616 XRT_MAYBE_UNUSED xrt_result_t xret = u_pa_factory_create(&upaf); 617 assert(xret == XRT_SUCCESS && upaf != NULL); 618 619 return comp_multi_create_system_compositor(&c->base.base, upaf, &c->sys_info, false, out_xsysc); 620}