The open source OpenXR runtime
at mr/scanout-values 598 lines 19 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 // UUIDs and LUID already set in vk init. 275 (void)sys_info->compositor_vk_deviceUUID; 276 (void)sys_info->client_vk_deviceUUID; 277 (void)sys_info->client_d3d_deviceLUID; 278 (void)sys_info->client_d3d_deviceLUID_valid; 279 uint32_t view_count = xdev->hmd->view_count; 280 // clang-format off 281 for (uint32_t i = 0; i < view_count; ++i) { 282 sys_info->views[i].recommended.width_pixels = RECOMMENDED_VIEW_WIDTH; 283 sys_info->views[i].recommended.height_pixels = RECOMMENDED_VIEW_HEIGHT; 284 sys_info->views[i].recommended.sample_count = 1; 285 sys_info->views[i].max.width_pixels = MAX_VIEW_WIDTH; 286 sys_info->views[i].max.height_pixels = MAX_VIEW_HEIGHT; 287 sys_info->views[i].max.sample_count = 1; 288 } 289 // clang-format on 290 291 // Copy the list directly. 292 assert(xdev->hmd->blend_mode_count <= XRT_MAX_DEVICE_BLEND_MODES); 293 assert(xdev->hmd->blend_mode_count != 0); 294 assert(xdev->hmd->blend_mode_count <= ARRAY_SIZE(sys_info->supported_blend_modes)); 295 for (size_t i = 0; i < xdev->hmd->blend_mode_count; ++i) { 296 assert(u_verify_blend_mode_valid(xdev->hmd->blend_modes[i])); 297 sys_info->supported_blend_modes[i] = xdev->hmd->blend_modes[i]; 298 } 299 sys_info->supported_blend_mode_count = (uint8_t)xdev->hmd->blend_mode_count; 300 301 // Refresh rates. 302 sys_info->refresh_rate_count = 1; 303 sys_info->refresh_rates_hz[0] = (float)(1. / time_ns_to_s(c->settings.frame_interval_ns)); 304 305 return true; 306} 307 308 309/* 310 * 311 * Member functions. 312 * 313 */ 314 315static xrt_result_t 316null_compositor_begin_session(struct xrt_compositor *xc, const struct xrt_begin_session_info *type) 317{ 318 struct null_compositor *c = null_compositor(xc); 319 NULL_DEBUG(c, "BEGIN_SESSION"); 320 321 /* 322 * No logic needed here for the null compositor, if using the null 323 * compositor as a base for a new compositor put desired logic here. 324 */ 325 326 return XRT_SUCCESS; 327} 328 329static xrt_result_t 330null_compositor_end_session(struct xrt_compositor *xc) 331{ 332 struct null_compositor *c = null_compositor(xc); 333 NULL_DEBUG(c, "END_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_predict_frame(struct xrt_compositor *xc, 345 int64_t *out_frame_id, 346 int64_t *out_wake_time_ns, 347 int64_t *out_predicted_gpu_time_ns, 348 int64_t *out_predicted_display_time_ns, 349 int64_t *out_predicted_display_period_ns) 350{ 351 COMP_TRACE_MARKER(); 352 353 struct null_compositor *c = null_compositor(xc); 354 NULL_TRACE(c, "PREDICT_FRAME"); 355 356 int64_t now_ns = os_monotonic_get_ns(); 357 int64_t null_desired_present_time_ns = 0; 358 int64_t null_present_slop_ns = 0; 359 int64_t null_min_display_period_ns = 0; 360 361 u_pc_predict( // 362 c->upc, // upc 363 now_ns, // now_ns 364 out_frame_id, // out_frame_id 365 out_wake_time_ns, // out_wake_up_time_ns 366 &null_desired_present_time_ns, // out_desired_present_time_ns 367 &null_present_slop_ns, // out_present_slop_ns 368 out_predicted_display_time_ns, // out_predicted_display_time_ns 369 out_predicted_display_period_ns, // out_predicted_display_period_ns 370 &null_min_display_period_ns); // out_min_display_period_ns 371 372 return XRT_SUCCESS; 373} 374 375static xrt_result_t 376null_compositor_mark_frame(struct xrt_compositor *xc, 377 int64_t frame_id, 378 enum xrt_compositor_frame_point point, 379 int64_t when_ns) 380{ 381 COMP_TRACE_MARKER(); 382 383 struct null_compositor *c = null_compositor(xc); 384 NULL_TRACE(c, "MARK_FRAME %i", point); 385 386 switch (point) { 387 case XRT_COMPOSITOR_FRAME_POINT_WOKE: 388 u_pc_mark_point(c->upc, U_TIMING_POINT_WAKE_UP, frame_id, when_ns); 389 return XRT_SUCCESS; 390 default: assert(false); 391 } 392 393 return XRT_SUCCESS; 394} 395 396static xrt_result_t 397null_compositor_begin_frame(struct xrt_compositor *xc, int64_t frame_id) 398{ 399 struct null_compositor *c = null_compositor(xc); 400 NULL_TRACE(c, "BEGIN_FRAME"); 401 402 /* 403 * No logic needed here for the null compositor, if using the null 404 * compositor as a base for a new compositor put desired logic here. 405 */ 406 407 return XRT_SUCCESS; 408} 409 410static xrt_result_t 411null_compositor_discard_frame(struct xrt_compositor *xc, int64_t frame_id) 412{ 413 struct null_compositor *c = null_compositor(xc); 414 NULL_TRACE(c, "DISCARD_FRAME"); 415 416 // Shouldn't be called. 417 assert(false); 418 419 return XRT_SUCCESS; 420} 421 422static xrt_result_t 423null_compositor_layer_commit(struct xrt_compositor *xc, xrt_graphics_sync_handle_t sync_handle) 424{ 425 COMP_TRACE_MARKER(); 426 427 struct null_compositor *c = null_compositor(xc); 428 NULL_TRACE(c, "LAYER_COMMIT"); 429 430 int64_t frame_id = c->base.layer_accum.data.frame_id; 431 int64_t display_time_ns = c->base.layer_accum.layers[0].data.timestamp; 432 433 // Default value from monado, overridden by HMD device where possible. 434 struct xrt_vec3 default_eye_relation = {0.063f, 0.f, 0.f}; 435 struct xrt_space_relation head_relation = {0}; 436 437 struct xrt_fov fovs[2] = {0}; 438 struct xrt_pose poses[2] = {0}; 439 xrt_result_t xret = 440 xrt_device_get_view_poses(c->xdev, &default_eye_relation, display_time_ns, 2, &head_relation, fovs, poses); 441 if (xret != XRT_SUCCESS) { 442 return xret; 443 } 444 445 446 /* 447 * The null compositor doesn't render any frames, but needs to do 448 * minimal bookkeeping and handling of arguments. If using the null 449 * compositor as a base for a new compositor this is where you render 450 * frames to be displayed to devices or remote clients. 451 */ 452 453 // If you are using the system/multi-compositor (multiple client module), your native compositor 454 // can just unref the sync handle. Otherwise please use it. 455 u_graphics_sync_unref(&sync_handle); 456 457 /* 458 * Time keeping needed to keep the pacer happy. 459 */ 460 461 // When we begin rendering. 462 { 463 int64_t now_ns = os_monotonic_get_ns(); 464 u_pc_mark_point(c->upc, U_TIMING_POINT_BEGIN, frame_id, now_ns); 465 } 466 467 // When we are submitting to the GPU. 468 { 469 int64_t now_ns = os_monotonic_get_ns(); 470 u_pc_mark_point(c->upc, U_TIMING_POINT_SUBMIT_BEGIN, frame_id, now_ns); 471 472 now_ns = os_monotonic_get_ns(); 473 u_pc_mark_point(c->upc, U_TIMING_POINT_SUBMIT_END, frame_id, now_ns); 474 } 475 476 // Now is a good point to garbage collect. 477 comp_swapchain_shared_garbage_collect(&c->base.cscs); 478 479 return XRT_SUCCESS; 480} 481 482static void 483null_compositor_destroy(struct xrt_compositor *xc) 484{ 485 struct null_compositor *c = null_compositor(xc); 486 struct vk_bundle *vk = get_vk(c); 487 488 NULL_DEBUG(c, "NULL_COMP_DESTROY"); 489 490 // Make sure we don't have anything to destroy. 491 comp_swapchain_shared_garbage_collect(&c->base.cscs); 492 493 // Must be destroyed before Vulkan. 494 comp_swapchain_shared_destroy(&c->base.cscs, vk); 495 496 if (vk->device != VK_NULL_HANDLE) { 497 vk->vkDestroyDevice(vk->device, NULL); 498 vk->device = VK_NULL_HANDLE; 499 } 500 501 vk_deinit_mutex(vk); 502 503 if (vk->instance != VK_NULL_HANDLE) { 504 vk->vkDestroyInstance(vk->instance, NULL); 505 vk->instance = VK_NULL_HANDLE; 506 } 507 508 comp_base_fini(&c->base); 509 510 u_pc_destroy(&c->upc); 511 512 free(c); 513} 514 515static xrt_result_t 516null_compositor_get_display_refresh_rate(struct xrt_compositor *xc, float *out_display_refresh_rate_hz) 517{ 518 struct null_compositor *c = null_compositor(xc); 519 520 *out_display_refresh_rate_hz = c->sys_info.refresh_rates_hz[0]; 521 return XRT_SUCCESS; 522} 523 524static xrt_result_t 525null_compositor_request_display_refresh_rate(struct xrt_compositor *xc, float display_refresh_rate_hz) 526{ 527 return XRT_SUCCESS; 528} 529 530/* 531 * 532 * 'Exported' functions. 533 * 534 */ 535 536xrt_result_t 537null_compositor_create_system(struct xrt_device *xdev, struct xrt_system_compositor **out_xsysc) 538{ 539 struct null_compositor *c = U_TYPED_CALLOC(struct null_compositor); 540 541 struct xrt_compositor *iface = &c->base.base.base; 542 iface->begin_session = null_compositor_begin_session; 543 iface->end_session = null_compositor_end_session; 544 iface->predict_frame = null_compositor_predict_frame; 545 iface->mark_frame = null_compositor_mark_frame; 546 iface->begin_frame = null_compositor_begin_frame; 547 iface->discard_frame = null_compositor_discard_frame; 548 iface->layer_commit = null_compositor_layer_commit; 549 iface->destroy = null_compositor_destroy; 550 c->base.base.base.get_display_refresh_rate = null_compositor_get_display_refresh_rate; 551 c->base.base.base.request_display_refresh_rate = null_compositor_request_display_refresh_rate; 552 c->settings.log_level = debug_get_log_option_log(); 553 c->frame.waited.id = -1; 554 c->frame.rendering.id = -1; 555 c->settings.frame_interval_ns = U_TIME_1S_IN_NS / 20; // 20 FPS 556 c->xdev = xdev; 557 558 NULL_DEBUG(c, "Doing init %p", (void *)c); 559 560 NULL_INFO(c, 561 "\n" 562 "################################################################################\n" 563 "# Null compositor starting, if you intended to use the null compositor (for CI #\n" 564 "# integration) then everything is mostly likely setup correctly. But if you #\n" 565 "# intended to use Monado with real hardware it you probably built Monado #\n" 566 "# without the main compositor, please check your build config and make sure #\n" 567 "# that the main compositor is being built. Also make sure that the environment #\n" 568 "# variable XRT_COMPOSITOR_NULL is not set. #\n" 569 "################################################################################"); 570 571 // Do this as early as possible 572 comp_base_init(&c->base); 573 574 575 /* 576 * Main init sequence. 577 */ 578 579 if (!compositor_init_pacing(c) || // 580 !compositor_init_vulkan(c) || // 581 !compositor_init_sys_info(c, xdev) || // 582 !compositor_init_info(c)) { // 583 NULL_DEBUG(c, "Failed to init compositor %p", (void *)c); 584 c->base.base.base.destroy(&c->base.base.base); 585 586 return XRT_ERROR_VULKAN; 587 } 588 589 590 NULL_DEBUG(c, "Done %p", (void *)c); 591 592 // Standard app pacer. 593 struct u_pacing_app_factory *upaf = NULL; 594 XRT_MAYBE_UNUSED xrt_result_t xret = u_pa_factory_create(&upaf); 595 assert(xret == XRT_SUCCESS && upaf != NULL); 596 597 return comp_multi_create_system_compositor(&c->base.base, upaf, &c->sys_info, false, out_xsysc); 598}