The open source OpenXR runtime
at mr/scanout-values 1250 lines 37 kB view raw
1// Copyright 2019-2024, Collabora, Ltd. 2// Copyright 2024-2025, NVIDIA CORPORATION. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief Compositor rendering code. 7 * @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com> 8 * @author Jakob Bornecrantz <jakob@collabora.com> 9 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 10 * @author Moshi Turner <moshiturner@protonmail.com> 11 * @ingroup comp_main 12 */ 13 14#include "render/render_interface.h" 15#include "xrt/xrt_defines.h" 16#include "xrt/xrt_frame.h" 17#include "xrt/xrt_compositor.h" 18#include "xrt/xrt_results.h" 19 20#include "os/os_time.h" 21 22#include "math/m_api.h" 23#include "math/m_matrix_2x2.h" 24#include "math/m_space.h" 25 26#include "util/u_misc.h" 27#include "util/u_trace_marker.h" 28#include "util/u_distortion_mesh.h" 29#include "util/u_sink.h" 30#include "util/u_var.h" 31#include "util/u_frame_times_widget.h" 32#include "util/u_debug.h" 33 34#include "util/comp_render.h" 35#include "util/comp_high_level_render.h" 36 37#include "main/comp_frame.h" 38#include "main/comp_mirror_to_debug_gui.h" 39 40#ifdef XRT_FEATURE_WINDOW_PEEK 41#include "main/comp_window_peek.h" 42#endif 43 44#include "vk/vk_helpers.h" 45#include "vk/vk_cmd.h" 46#include "vk/vk_image_readback_to_xf_pool.h" 47 48#include <string.h> 49#include <stdlib.h> 50#include <stdio.h> 51#include <assert.h> 52#include <math.h> 53 54DEBUG_GET_ONCE_LOG_OPTION(comp_frame_lag_level, "XRT_COMP_FRAME_LAG_LOG_AS_LEVEL", U_LOGGING_WARN) 55#define LOG_FRAME_LAG(...) U_LOG_IFL(debug_get_log_option_comp_frame_lag_level(), u_log_get_global_level(), __VA_ARGS__) 56 57/* 58 * 59 * Small internal helpers. 60 * 61 */ 62 63#define CHAIN(STRUCT, NEXT) \ 64 do { \ 65 (STRUCT).pNext = NEXT; \ 66 NEXT = (VkBaseInStructure *)&(STRUCT); \ 67 } while (false) 68 69 70/* 71 * 72 * Private struct(s). 73 * 74 */ 75 76/*! 77 * What is the source of the FoV values used for the final image that the 78 * compositor produces and is sent to the hardware (or software). 79 */ 80enum comp_target_fov_source 81{ 82 /*! 83 * The FoV values used for the final target is taken from the 84 * distortion information on the @ref xrt_hmd_parts struct. 85 */ 86 COMP_TARGET_FOV_SOURCE_DISTORTION, 87 88 /*! 89 * The FoV values used for the final target is taken from the 90 * those returned from the device's get_views. 91 */ 92 COMP_TARGET_FOV_SOURCE_DEVICE_VIEWS, 93}; 94 95/*! 96 * Holds associated vulkan objects and state to render with a distortion. 97 * 98 * @ingroup comp_main 99 */ 100struct comp_renderer 101{ 102 //! @name Durable members 103 //! @brief These don't require the images to be created and don't depend on it. 104 //! @{ 105 106 //! The compositor we were created by 107 struct comp_compositor *c; 108 struct comp_settings *settings; 109 110 struct comp_mirror_to_debug_gui mirror_to_debug_gui; 111 112 //! @} 113 114 //! @name Image-dependent members 115 //! @{ 116 117 //! Index of the current buffer/image 118 int32_t acquired_buffer; 119 120 //! Which buffer was last submitted and has a fence pending. 121 int32_t fenced_buffer; 122 123 /*! 124 * The render pass used to render to the target, it depends on the 125 * target's format so will be recreated each time the target changes. 126 */ 127 struct render_gfx_render_pass target_render_pass; 128 129 /*! 130 * Array of "rendering" target resources equal in size to the number of 131 * comp_target images. Each target resources holds all of the resources 132 * needed to render to that target and its views. 133 */ 134 struct render_gfx_target_resources *rtr_array; 135 136 /*! 137 * Array of fences equal in size to the number of comp_target images. 138 */ 139 VkFence *fences; 140 141 /*! 142 * The number of renderings/fences we've created: set from comp_target when we use that data. 143 */ 144 uint32_t buffer_count; 145 146 //! @} 147}; 148 149 150/* 151 * 152 * Functions. 153 * 154 */ 155 156static void 157renderer_wait_queue_idle(struct comp_renderer *r) 158{ 159 COMP_TRACE_MARKER(); 160 struct vk_bundle *vk = &r->c->base.vk; 161 162 vk_queue_lock(vk->main_queue); 163 vk->vkQueueWaitIdle(vk->main_queue->queue); 164 vk_queue_unlock(vk->main_queue); 165} 166 167static void 168calc_viewport_data(struct comp_renderer *r, 169 struct render_viewport_data out_viewport_data[XRT_MAX_VIEWS], 170 size_t view_count) 171{ 172 struct comp_compositor *c = r->c; 173 174 bool pre_rotate = false; 175 if (r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || 176 r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { 177 COMP_SPEW(c, "Swapping width and height, since we are pre rotating"); 178 pre_rotate = true; 179 } 180 181 int w_i32 = pre_rotate ? r->c->xdev->hmd->screens[0].h_pixels : r->c->xdev->hmd->screens[0].w_pixels; 182 int h_i32 = pre_rotate ? r->c->xdev->hmd->screens[0].w_pixels : r->c->xdev->hmd->screens[0].h_pixels; 183 184 float scale_x = (float)r->c->target->width / (float)w_i32; 185 float scale_y = (float)r->c->target->height / (float)h_i32; 186 187 for (uint32_t i = 0; i < view_count; ++i) { 188 struct xrt_view *v = &r->c->xdev->hmd->views[i]; 189 if (pre_rotate) { 190 out_viewport_data[i] = (struct render_viewport_data){ 191 .x = (uint32_t)(v->viewport.y_pixels * scale_x), 192 .y = (uint32_t)(v->viewport.x_pixels * scale_y), 193 .w = (uint32_t)(v->viewport.h_pixels * scale_x), 194 .h = (uint32_t)(v->viewport.w_pixels * scale_y), 195 }; 196 } else { 197 out_viewport_data[i] = (struct render_viewport_data){ 198 .x = (uint32_t)(v->viewport.x_pixels * scale_x), 199 .y = (uint32_t)(v->viewport.y_pixels * scale_y), 200 .w = (uint32_t)(v->viewport.w_pixels * scale_x), 201 .h = (uint32_t)(v->viewport.h_pixels * scale_y), 202 }; 203 } 204 } 205} 206 207static void 208calc_vertex_rot_data(struct comp_renderer *r, struct xrt_matrix_2x2 out_vertex_rots[XRT_MAX_VIEWS], size_t view_count) 209{ 210 bool pre_rotate = false; 211 if (r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || 212 r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { 213 COMP_SPEW(r->c, "Swapping width and height, since we are pre rotating"); 214 pre_rotate = true; 215 } 216 217 const struct xrt_matrix_2x2 rotation_90_cw = {{ 218 .vecs = 219 { 220 {0, 1}, 221 {-1, 0}, 222 }, 223 }}; 224 225 for (uint32_t i = 0; i < view_count; i++) { 226 // Get the view. 227 struct xrt_view *v = &r->c->xdev->hmd->views[i]; 228 229 // Copy data. 230 struct xrt_matrix_2x2 rot = v->rot; 231 232 // Should we rotate. 233 if (pre_rotate) { 234 m_mat2x2_multiply(&rot, &rotation_90_cw, &rot); 235 } 236 237 out_vertex_rots[i] = rot; 238 } 239} 240 241static void 242calc_pose_data(struct comp_renderer *r, 243 enum comp_target_fov_source fov_source, 244 struct xrt_fov out_fovs[XRT_MAX_VIEWS], 245 struct xrt_pose out_world_scanout_begin[XRT_MAX_VIEWS], 246 struct xrt_pose out_world_scanout_end[XRT_MAX_VIEWS], 247 struct xrt_pose out_eye[XRT_MAX_VIEWS], 248 uint32_t view_count) 249{ 250 COMP_TRACE_MARKER(); 251 252 struct xrt_vec3 default_eye_relation = { 253 0.063000f, /*! @todo get actual ipd_meters */ 254 0.0f, 255 0.0f, 256 }; 257 258 struct xrt_space_relation head_relation[2] = XRT_SPACE_RELATION_ZERO; 259 struct xrt_fov xdev_fovs[XRT_MAX_VIEWS] = XRT_STRUCT_INIT; 260 struct xrt_pose xdev_poses[2][XRT_MAX_VIEWS] = XRT_STRUCT_INIT; 261 262 uint64_t scanout_time_ns = 0; 263 if (r->c->xdev->hmd->screens[0].scanout_direction == XRT_SCANOUT_DIRECTION_TOP_TO_BOTTOM) { 264 scanout_time_ns = r->c->xdev->hmd->screens[0].scanout_time_ns; 265 } else if (r->c->xdev->hmd->screens[0].scanout_direction != XRT_SCANOUT_DIRECTION_NONE) { 266 COMP_SPEW(r->c, "Unable to apply scanout compensation as only DIRECTION_TOP_TO_BOTTOM is supported"); 267 } 268 269 int64_t begin_timestamp_ns = r->c->frame.rendering.predicted_display_time_ns; 270 int64_t end_timestamp_ns = begin_timestamp_ns + scanout_time_ns; 271 272 // Pose at beginning of scanout 273 xrt_result_t xret = xrt_device_get_view_poses( // 274 r->c->xdev, // 275 &default_eye_relation, // 276 begin_timestamp_ns, // at_timestamp_ns 277 view_count, // 278 &head_relation[0], // out_head_relation 279 xdev_fovs, // out_fovs 280 xdev_poses[0]); 281 if (xret != XRT_SUCCESS) { 282 struct u_pp_sink_stack_only sink; 283 u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); 284 u_pp_xrt_result(dg, xret); 285 U_LOG_E("xrt_device_get_view_poses failed: %s", sink.buffer); 286 return; 287 } 288 289 // Pose at end of scanout 290 if (scanout_time_ns != 0) { 291 xret = xrt_device_get_view_poses( // 292 r->c->xdev, // 293 &default_eye_relation, // 294 end_timestamp_ns, // at_timestamp_ns 295 view_count, // 296 &head_relation[1], // out_head_relation 297 xdev_fovs, // out_fovs 298 xdev_poses[1]); // out_poses 299 if (xret != XRT_SUCCESS) { 300 struct u_pp_sink_stack_only sink; 301 u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink); 302 u_pp_xrt_result(dg, xret); 303 U_LOG_E("xrt_device_get_view_poses failed: %s", sink.buffer); 304 return; 305 } 306 } else { 307 for (size_t i = 0; i < XRT_MAX_VIEWS; ++i) { 308 xdev_poses[1][i] = xdev_poses[0][i]; 309 } 310 head_relation[1] = head_relation[0]; 311 } 312 313 struct xrt_fov dist_fov[XRT_MAX_VIEWS] = XRT_STRUCT_INIT; 314 for (uint32_t i = 0; i < view_count; i++) { 315 dist_fov[i] = r->c->xdev->hmd->distortion.fov[i]; 316 } 317 318 bool use_xdev = false; // Probably what we want. 319 320 switch (fov_source) { 321 case COMP_TARGET_FOV_SOURCE_DISTORTION: use_xdev = false; break; 322 case COMP_TARGET_FOV_SOURCE_DEVICE_VIEWS: use_xdev = true; break; 323 } 324 325 for (uint32_t i = 0; i < view_count; i++) { 326 const struct xrt_fov fov = use_xdev ? xdev_fovs[i] : dist_fov[i]; 327 const struct xrt_pose eye_pose_scanout_start = xdev_poses[0][i]; 328 const struct xrt_pose eye_pose_scanout_end = xdev_poses[1][i]; 329 330 struct xrt_space_relation result_scanout_start = {0}; 331 struct xrt_space_relation result_scanout_end = {0}; 332 struct xrt_relation_chain xrc = {0}; 333 334 m_relation_chain_push_pose_if_not_identity(&xrc, &eye_pose_scanout_start); 335 m_relation_chain_push_relation(&xrc, &head_relation[0]); 336 m_relation_chain_resolve(&xrc, &result_scanout_start); 337 338 xrc = (struct xrt_relation_chain){0}; 339 340 m_relation_chain_push_pose_if_not_identity(&xrc, &eye_pose_scanout_end); 341 m_relation_chain_push_relation(&xrc, &head_relation[1]); 342 m_relation_chain_resolve(&xrc, &result_scanout_end); 343 344 // Results to callers. 345 out_fovs[i] = fov; 346 out_world_scanout_begin[i] = result_scanout_start.pose; 347 out_world_scanout_end[i] = result_scanout_end.pose; 348 out_eye[i] = eye_pose_scanout_start; 349 350 // For remote rendering targets. 351 r->c->base.frame_params.fovs[i] = fov; 352 r->c->base.frame_params.poses[i] = result_scanout_start.pose; 353 } 354} 355 356//! @pre comp_target_has_images(r->c->target) 357static void 358renderer_build_rendering_target_resources(struct comp_renderer *r, 359 struct render_gfx_target_resources *rtr, 360 uint32_t index) 361{ 362 COMP_TRACE_MARKER(); 363 364 struct comp_compositor *c = r->c; 365 366 VkImageView image_view = r->c->target->images[index].view; 367 VkExtent2D extent = {r->c->target->width, r->c->target->height}; 368 369 render_gfx_target_resources_init( // 370 rtr, // 371 &c->nr, // 372 &r->target_render_pass, // 373 image_view, // 374 extent); // 375} 376 377/*! 378 * @pre comp_target_has_images(r->c->target) 379 * Update r->buffer_count before calling. 380 */ 381static void 382renderer_create_renderings_and_fences(struct comp_renderer *r) 383{ 384 assert(r->fences == NULL); 385 if (r->buffer_count == 0) { 386 COMP_ERROR(r->c, "Requested 0 command buffers."); 387 return; 388 } 389 390 COMP_DEBUG(r->c, "Allocating %d Command Buffers.", r->buffer_count); 391 392 struct vk_bundle *vk = &r->c->base.vk; 393 394 bool use_compute = r->settings->use_compute; 395 if (!use_compute) { 396 r->rtr_array = U_TYPED_ARRAY_CALLOC(struct render_gfx_target_resources, r->buffer_count); 397 398 render_gfx_render_pass_init( // 399 &r->target_render_pass, // rgrp 400 &r->c->nr, // struct render_resources 401 r->c->target->format, // 402 VK_ATTACHMENT_LOAD_OP_CLEAR, // load_op 403 r->c->target->final_layout); // final_layout 404 405 for (uint32_t i = 0; i < r->buffer_count; ++i) { 406 renderer_build_rendering_target_resources(r, &r->rtr_array[i], i); 407 } 408 } 409 410 r->fences = U_TYPED_ARRAY_CALLOC(VkFence, r->buffer_count); 411 412 for (uint32_t i = 0; i < r->buffer_count; i++) { 413 VkFenceCreateInfo fence_info = { 414 .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 415 .flags = VK_FENCE_CREATE_SIGNALED_BIT, 416 }; 417 418 VkResult ret = vk->vkCreateFence( // 419 vk->device, // 420 &fence_info, // 421 NULL, // 422 &r->fences[i]); // 423 if (ret != VK_SUCCESS) { 424 COMP_ERROR(r->c, "vkCreateFence: %s", vk_result_string(ret)); 425 } 426 427 char buf[] = "Comp Renderer X_XXXX_XXXX"; 428 snprintf(buf, ARRAY_SIZE(buf), "Comp Renderer %u", i); 429 VK_NAME_FENCE(vk, r->fences[i], buf); 430 } 431} 432 433static void 434renderer_close_renderings_and_fences(struct comp_renderer *r) 435{ 436 struct vk_bundle *vk = &r->c->base.vk; 437 // Renderings 438 if (r->buffer_count > 0 && r->rtr_array != NULL) { 439 for (uint32_t i = 0; i < r->buffer_count; i++) { 440 render_gfx_target_resources_fini(&r->rtr_array[i]); 441 } 442 443 // Close the render pass used for rendering to the target. 444 render_gfx_render_pass_fini(&r->target_render_pass); 445 446 free(r->rtr_array); 447 r->rtr_array = NULL; 448 } 449 450 // Fences 451 if (r->buffer_count > 0 && r->fences != NULL) { 452 for (uint32_t i = 0; i < r->buffer_count; i++) { 453 vk->vkDestroyFence(vk->device, r->fences[i], NULL); 454 r->fences[i] = VK_NULL_HANDLE; 455 } 456 free(r->fences); 457 r->fences = NULL; 458 } 459 460 r->buffer_count = 0; 461 r->acquired_buffer = -1; 462 r->fenced_buffer = -1; 463} 464 465/*! 466 * @brief Ensure that target images and renderings are created, if possible. 467 * 468 * @param r Self pointer 469 * @param force_recreate If true, will tear down and re-create images and renderings, e.g. for a resize 470 * 471 * @returns true if images and renderings are ready and created. 472 * 473 * @private @memberof comp_renderer 474 * @ingroup comp_main 475 */ 476static bool 477renderer_ensure_images_and_renderings(struct comp_renderer *r, bool force_recreate) 478{ 479 struct comp_compositor *c = r->c; 480 struct comp_target *target = c->target; 481 482 if (!comp_target_check_ready(target)) { 483 // Not ready, so can't render anything. 484 return false; 485 } 486 487 // We will create images if we don't have any images or if we were told to recreate them. 488 bool create = force_recreate || !comp_target_has_images(target) || (r->buffer_count == 0); 489 if (!create) { 490 return true; 491 } 492 493 COMP_DEBUG(c, "Creating images and renderings (force_recreate: %s).", force_recreate ? "true" : "false"); 494 495 /* 496 * This makes sure that any pending command buffer has completed 497 * and all resources referred by it can now be manipulated. This 498 * make sure that validation doesn't complain. This is done 499 * during resize so isn't time critical. 500 */ 501 renderer_wait_queue_idle(r); 502 503 // Make we sure we destroy all dependent things before creating new images. 504 renderer_close_renderings_and_fences(r); 505 506 VkImageUsageFlags image_usage = 0; 507 if (r->settings->use_compute) { 508 image_usage |= VK_IMAGE_USAGE_STORAGE_BIT; 509 } else { 510 image_usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 511 } 512 513 if (c->peek) { 514 image_usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; 515 } 516 517 struct comp_target_create_images_info info = { 518 .extent = 519 { 520 .width = r->c->settings.preferred.width, 521 .height = r->c->settings.preferred.height, 522 }, 523 .image_usage = image_usage, 524 .color_space = r->settings->color_space, 525 .present_mode = r->settings->present_mode, 526 }; 527 528 static_assert(ARRAY_SIZE(info.formats) == ARRAY_SIZE(r->c->settings.formats), "Miss-match format array sizes"); 529 for (uint32_t i = 0; i < r->c->settings.format_count; i++) { 530 info.formats[info.format_count++] = r->c->settings.formats[i]; 531 } 532 533 comp_target_create_images(r->c->target, &info); 534 535 bool pre_rotate = false; 536 if (r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || 537 r->c->target->surface_transform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR) { 538 pre_rotate = true; 539 } 540 541 // @todo: is it safe to fail here? 542 if (!render_distortion_images_ensure(&r->c->nr, &r->c->base.vk, r->c->xdev, pre_rotate)) 543 return false; 544 545 r->buffer_count = r->c->target->image_count; 546 547 renderer_create_renderings_and_fences(r); 548 549 assert(r->buffer_count != 0); 550 551 return true; 552} 553 554//! Create renderer and initialize non-image-dependent members 555static void 556renderer_init(struct comp_renderer *r, struct comp_compositor *c, VkExtent2D scratch_extent) 557{ 558 COMP_TRACE_MARKER(); 559 560 r->c = c; 561 r->settings = &c->settings; 562 563 r->acquired_buffer = -1; 564 r->fenced_buffer = -1; 565 r->rtr_array = NULL; 566 567 // Setup the scratch images. 568 bool bret = chl_scratch_ensure( // 569 &c->scratch, // scratch 570 &c->nr, // struct render_resources 571 c->nr.view_count, // view_count 572 scratch_extent, // extent 573 VK_FORMAT_R8G8B8A8_SRGB); // format 574 if (!bret) { 575 COMP_ERROR(c, "chl_scratch_ensure: false"); 576 assert(bret && "Whelp, can't return a error. But should never really fail."); 577 } 578 579 // Try to early-allocate these, in case we can. 580 renderer_ensure_images_and_renderings(r, false); 581 582 struct vk_bundle *vk = &r->c->base.vk; 583 584 VkResult ret = comp_mirror_init( // 585 &r->mirror_to_debug_gui, // 586 vk, // 587 &c->shaders, // 588 scratch_extent); // 589 if (ret != VK_SUCCESS) { 590 COMP_ERROR(c, "comp_mirror_init: %s", vk_result_string(ret)); 591 assert(false && "Whelp, can't return a error. But should never really fail."); 592 } 593} 594 595static void 596renderer_wait_for_last_fence(struct comp_renderer *r) 597{ 598 COMP_TRACE_MARKER(); 599 600 if (r->fenced_buffer < 0) { 601 return; 602 } 603 604 struct vk_bundle *vk = &r->c->base.vk; 605 VkResult ret; 606 607 ret = vk->vkWaitForFences(vk->device, 1, &r->fences[r->fenced_buffer], VK_TRUE, UINT64_MAX); 608 if (ret != VK_SUCCESS) { 609 COMP_ERROR(r->c, "vkWaitForFences: %s", vk_result_string(ret)); 610 } 611 612 r->fenced_buffer = -1; 613} 614 615static XRT_CHECK_RESULT VkResult 616renderer_submit_queue(struct comp_renderer *r, VkCommandBuffer cmd, VkPipelineStageFlags pipeline_stage_flag) 617{ 618 COMP_TRACE_MARKER(); 619 620 struct vk_bundle *vk = &r->c->base.vk; 621 int64_t frame_id = r->c->frame.rendering.id; 622 VkResult ret; 623 624 assert(frame_id >= 0); 625 626 627 /* 628 * Wait for previous frame's work to complete. 629 */ 630 631 // Wait for the last fence, if any. 632 renderer_wait_for_last_fence(r); 633 assert(r->fenced_buffer < 0); 634 635 assert(r->acquired_buffer >= 0); 636 ret = vk->vkResetFences(vk->device, 1, &r->fences[r->acquired_buffer]); 637 VK_CHK_AND_RET(ret, "vkResetFences"); 638 639 640 /* 641 * Regular semaphore setup. 642 */ 643 644 // Convenience. 645 struct comp_target *ct = r->c->target; 646#define WAIT_SEMAPHORE_COUNT 1 647 648 VkSemaphore wait_sems[WAIT_SEMAPHORE_COUNT] = {ct->semaphores.present_complete}; 649 VkPipelineStageFlags stage_flags[WAIT_SEMAPHORE_COUNT] = {pipeline_stage_flag}; 650 651 VkSemaphore *wait_sems_ptr = NULL; 652 VkPipelineStageFlags *stage_flags_ptr = NULL; 653 uint32_t wait_sem_count = 0; 654 if (wait_sems[0] != VK_NULL_HANDLE) { 655 wait_sems_ptr = wait_sems; 656 stage_flags_ptr = stage_flags; 657 wait_sem_count = WAIT_SEMAPHORE_COUNT; 658 } 659 660#define SIGNAL_SEMAPHRE_COUNT 1 661 VkSemaphore signal_sems[SIGNAL_SEMAPHRE_COUNT] = {ct->semaphores.render_complete}; 662 663 uint32_t signal_sem_count = 0; 664 VkSemaphore *signal_sems_ptr = NULL; 665 if (signal_sems[0] != VK_NULL_HANDLE) { 666 signal_sems_ptr = signal_sems; 667 signal_sem_count = SIGNAL_SEMAPHRE_COUNT; 668 } 669 670 // Next pointer for VkSubmitInfo 671 const void *next = NULL; 672 673#ifdef VK_KHR_timeline_semaphore 674 assert(!comp_frame_is_invalid_locked(&r->c->frame.rendering)); 675 uint64_t render_complete_signal_values[SIGNAL_SEMAPHRE_COUNT] = {(uint64_t)frame_id}; 676 677 VkTimelineSemaphoreSubmitInfoKHR timeline_info = { 678 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, 679 }; 680 681 if (ct->semaphores.render_complete_is_timeline) { 682 timeline_info = (VkTimelineSemaphoreSubmitInfoKHR){ 683 .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR, 684 .signalSemaphoreValueCount = signal_sem_count, 685 .pSignalSemaphoreValues = render_complete_signal_values, 686 }; 687 688 CHAIN(timeline_info, next); 689 } 690#endif 691 692 693 VkSubmitInfo comp_submit_info = { 694 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, 695 .pNext = next, 696 .pWaitDstStageMask = stage_flags_ptr, 697 .pWaitSemaphores = wait_sems_ptr, 698 .waitSemaphoreCount = wait_sem_count, 699 .commandBufferCount = 1, 700 .pCommandBuffers = &cmd, 701 .signalSemaphoreCount = signal_sem_count, 702 .pSignalSemaphores = signal_sems_ptr, 703 }; 704 705 // Everything prepared, now we are submitting. 706 comp_target_mark_submit_begin(ct, frame_id, os_monotonic_get_ns()); 707 708 /* 709 * The renderer command buffer pool is only accessed from one thread, 710 * this satisfies the `_locked` requirement of the function. This lets 711 * us avoid taking a lot of locks. The queue lock will be taken by 712 * @ref vk_cmd_submit_locked tho. 713 */ 714 ret = vk_cmd_submit_locked(vk, vk->main_queue, 1, &comp_submit_info, r->fences[r->acquired_buffer]); 715 716 // We have now completed the submit, even if we failed. 717 comp_target_mark_submit_end(ct, frame_id, os_monotonic_get_ns()); 718 719 // Check after marking as submit complete. 720 VK_CHK_AND_RET(ret, "vk_cmd_submit_locked"); 721 722 // This buffer now have a pending fence. 723 r->fenced_buffer = r->acquired_buffer; 724 725 return ret; 726} 727 728static void 729renderer_acquire_swapchain_image(struct comp_renderer *r) 730{ 731 COMP_TRACE_MARKER(); 732 733 uint32_t buffer_index = 0; 734 VkResult ret; 735 736 assert(r->acquired_buffer < 0); 737 738 if (!renderer_ensure_images_and_renderings(r, false)) { 739 // Not ready yet. 740 return; 741 } 742 ret = comp_target_acquire(r->c->target, &buffer_index); 743 744 if ((ret == VK_ERROR_OUT_OF_DATE_KHR) || (ret == VK_SUBOPTIMAL_KHR)) { 745 COMP_DEBUG(r->c, "Received %s.", vk_result_string(ret)); 746 747 if (!renderer_ensure_images_and_renderings(r, true)) { 748 // Failed on force recreate. 749 COMP_ERROR(r->c, 750 "renderer_acquire_swapchain_image: comp_target_acquire was out of date, force " 751 "re-create image and renderings failed. Probably the target disappeared."); 752 return; 753 } 754 755 /* Acquire image again to silence validation error */ 756 ret = comp_target_acquire(r->c->target, &buffer_index); 757 if (ret != VK_SUCCESS) { 758 COMP_ERROR(r->c, "comp_target_acquire: %s", vk_result_string(ret)); 759 } 760 } else if (ret != VK_SUCCESS) { 761 COMP_ERROR(r->c, "comp_target_acquire: %s", vk_result_string(ret)); 762 } 763 764 r->acquired_buffer = buffer_index; 765} 766 767static void 768renderer_resize(struct comp_renderer *r) 769{ 770 if (!comp_target_check_ready(r->c->target)) { 771 // Can't create images right now. 772 // Just close any existing renderings. 773 renderer_close_renderings_and_fences(r); 774 return; 775 } 776 // Force recreate. 777 renderer_ensure_images_and_renderings(r, true); 778} 779 780static void 781renderer_present_swapchain_image(struct comp_renderer *r, uint64_t desired_present_time_ns, uint64_t present_slop_ns) 782{ 783 COMP_TRACE_MARKER(); 784 785 VkResult ret; 786 787 assert(!comp_frame_is_invalid_locked(&r->c->frame.rendering)); 788 uint64_t render_complete_signal_value = (uint64_t)r->c->frame.rendering.id; 789 790 ret = comp_target_present( // 791 r->c->target, // 792 r->c->base.vk.main_queue->queue, // 793 r->acquired_buffer, // 794 render_complete_signal_value, // 795 desired_present_time_ns, // 796 present_slop_ns); // 797 r->acquired_buffer = -1; 798 799 if (ret == VK_ERROR_OUT_OF_DATE_KHR || ret == VK_SUBOPTIMAL_KHR) { 800 renderer_resize(r); 801 return; 802 } 803 if (ret != VK_SUCCESS) { 804 COMP_ERROR(r->c, "vk_swapchain_present: %s", vk_result_string(ret)); 805 } 806} 807 808static void 809renderer_wait_for_present(struct comp_renderer *r, uint64_t desired_present_time_ns) 810{ 811 struct comp_compositor *c = r->c; 812 813 if (!comp_target_check_ready(c->target)) { 814 return; 815 } 816 817 // For estimating frame misses. 818 uint64_t before_ns = os_monotonic_get_ns(); 819 820 if (c->target->wait_for_present_supported) { 821 // reasonable timeout 822 time_duration_ns timeout_ns = c->frame_interval_ns * 2.5f; 823 824 // @note we don't actually care about the return value, just swallow errors, anything *critical* that 825 // may be returned will be handled quite soon by later calls 826 VkResult result = comp_target_wait_for_present(c->target, timeout_ns); 827 (void)result; 828 829 assert(result != VK_ERROR_EXTENSION_NOT_PRESENT); 830 } else { 831 /* 832 * For direct mode this makes us wait until the last frame has been 833 * actually shown to the user, this avoids us missing that we have 834 * missed a frame and miss-predicting the next frame. 835 * 836 * Not all drivers follow this behaviour, so KHR_present_wait 837 * should be preferred in all circumstances. 838 * 839 * Only do this if we are ready. 840 */ 841 842 // Do the acquire 843 renderer_acquire_swapchain_image(r); 844 } 845 846 // How long did it take? 847 uint64_t after_ns = os_monotonic_get_ns(); 848 849 /* 850 * Make sure we at least waited 1ms before warning. Then check 851 * if we are more then 1ms behind when we wanted to present. 852 */ 853 if (before_ns + U_TIME_1MS_IN_NS < after_ns && // 854 desired_present_time_ns + U_TIME_1MS_IN_NS < after_ns) { 855 uint64_t diff_ns = after_ns - desired_present_time_ns; 856 double diff_ms_f = time_ns_to_ms_f(diff_ns); 857 LOG_FRAME_LAG("Compositor probably missed frame by %.2fms", diff_ms_f); 858 } 859} 860 861static void 862renderer_fini(struct comp_renderer *r) 863{ 864 struct vk_bundle *vk = &r->c->base.vk; 865 866 // Command buffers 867 renderer_close_renderings_and_fences(r); 868 869 // Do before layer render just in case it holds any references. 870 comp_mirror_fini(&r->mirror_to_debug_gui, vk); 871 872 // Do this after the layer renderer. 873 chl_scratch_free_resources(&r->c->scratch, &r->c->nr); 874} 875 876 877/* 878 * 879 * Graphics 880 * 881 */ 882 883/*! 884 * @pre render_gfx_init(render, &c->nr) 885 */ 886static XRT_CHECK_RESULT VkResult 887dispatch_graphics(struct comp_renderer *r, 888 struct render_gfx *render, 889 struct chl_frame_state *frame_state, 890 enum comp_target_fov_source fov_source) 891{ 892 COMP_TRACE_MARKER(); 893 894 struct comp_compositor *c = r->c; 895 struct vk_bundle *vk = &c->base.vk; 896 VkResult ret; 897 898 // Basics 899 const struct comp_layer *layers = c->base.layer_accum.layers; 900 uint32_t layer_count = c->base.layer_accum.layer_count; 901 902 // Resources for the distortion render target. 903 struct render_gfx_target_resources *rtr = &r->rtr_array[r->acquired_buffer]; 904 905 // Viewport information. 906 struct render_viewport_data viewport_datas[XRT_MAX_VIEWS]; 907 calc_viewport_data(r, viewport_datas, render->r->view_count); 908 909 // Vertex rotation information. 910 struct xrt_matrix_2x2 vertex_rots[XRT_MAX_VIEWS]; 911 calc_vertex_rot_data(r, vertex_rots, render->r->view_count); 912 913 // Device view information. 914 struct xrt_fov fovs[XRT_MAX_VIEWS]; 915 struct xrt_pose world_poses_scanout_begin[XRT_MAX_VIEWS]; 916 struct xrt_pose world_poses_scanout_end[XRT_MAX_VIEWS]; 917 struct xrt_pose eye_poses[XRT_MAX_VIEWS]; 918 calc_pose_data( // 919 r, // 920 fov_source, // 921 fovs, // 922 world_poses_scanout_begin, // 923 world_poses_scanout_end, // 924 eye_poses, // 925 render->r->view_count); // 926 927 // Does everything. 928 chl_frame_state_gfx_default_pipeline( // 929 frame_state, // 930 render, // 931 layers, // 932 layer_count, // 933 world_poses_scanout_begin, // 934 eye_poses, // 935 fovs, // 936 rtr, // 937 viewport_datas, // 938 vertex_rots); // 939 940 // Everything is ready, submit to the queue. 941 ret = renderer_submit_queue(r, render->r->cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); 942 VK_CHK_AND_RET(ret, "renderer_submit_queue"); 943 944 return ret; 945} 946 947 948/* 949 * 950 * Compute 951 * 952 */ 953 954/*! 955 * @pre render_compute_init(render, &c->nr) 956 */ 957static XRT_CHECK_RESULT VkResult 958dispatch_compute(struct comp_renderer *r, 959 struct render_compute *render, 960 struct chl_frame_state *frame_state, 961 enum comp_target_fov_source fov_source) 962{ 963 COMP_TRACE_MARKER(); 964 965 struct comp_compositor *c = r->c; 966 struct vk_bundle *vk = &c->base.vk; 967 VkResult ret; 968 969 // Basics 970 const struct comp_layer *layers = c->base.layer_accum.layers; 971 uint32_t layer_count = c->base.layer_accum.layer_count; 972 973 // Device view information. 974 struct xrt_fov fovs[XRT_MAX_VIEWS]; 975 struct xrt_pose world_poses_scanout_begin[XRT_MAX_VIEWS]; 976 struct xrt_pose world_poses_scanout_end[XRT_MAX_VIEWS]; 977 struct xrt_pose eye_poses[XRT_MAX_VIEWS]; 978 calc_pose_data( // 979 r, // 980 fov_source, // 981 fovs, // 982 world_poses_scanout_begin, // 983 world_poses_scanout_end, // 984 eye_poses, // 985 render->r->view_count); // 986 987 // Target Vulkan resources.. 988 VkImage target_image = r->c->target->images[r->acquired_buffer].handle; 989 VkImageView target_storage_view = r->c->target->images[r->acquired_buffer].view; 990 991 // Target view information. 992 struct render_viewport_data target_viewport_datas[XRT_MAX_VIEWS]; 993 calc_viewport_data(r, target_viewport_datas, render->r->view_count); 994 995 // Does everything. 996 chl_frame_state_cs_default_pipeline( // 997 frame_state, // 998 render, // 999 layers, // 1000 layer_count, // 1001 world_poses_scanout_begin, // 1002 world_poses_scanout_end, // 1003 eye_poses, // 1004 fovs, // 1005 target_image, // 1006 target_storage_view, // 1007 target_viewport_datas); // 1008 1009 // Everything is ready, submit to the queue. 1010 ret = renderer_submit_queue(r, render->r->cmd, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT); 1011 VK_CHK_AND_RET(ret, "renderer_submit_queue"); 1012 1013 return ret; 1014} 1015 1016 1017/* 1018 * 1019 * Interface functions. 1020 * 1021 */ 1022 1023XRT_CHECK_RESULT xrt_result_t 1024comp_renderer_draw(struct comp_renderer *r) 1025{ 1026 COMP_TRACE_MARKER(); 1027 1028 struct comp_target *ct = r->c->target; 1029 struct comp_compositor *c = r->c; 1030 1031 // Check that we don't have any bad data. 1032 assert(!comp_frame_is_invalid_locked(&c->frame.waited)); 1033 assert(comp_frame_is_invalid_locked(&c->frame.rendering)); 1034 1035 // Move waited frame to rendering frame, clear waited. 1036 comp_frame_move_and_clear_locked(&c->frame.rendering, &c->frame.waited); 1037 1038 // Tell the target we are starting to render, for frame timing. 1039 comp_target_mark_begin(ct, c->frame.rendering.id, os_monotonic_get_ns()); 1040 1041 // Are we ready to render? No - skip rendering. 1042 if (!comp_target_check_ready(r->c->target)) { 1043 // Need to emulate rendering for the timing. 1044 //! @todo This should be discard. 1045 comp_target_mark_submit_begin(ct, c->frame.rendering.id, os_monotonic_get_ns()); 1046 comp_target_mark_submit_end(ct, c->frame.rendering.id, os_monotonic_get_ns()); 1047 1048 // Clear the rendering frame. 1049 comp_frame_clear_locked(&c->frame.rendering); 1050 return XRT_SUCCESS; 1051 } 1052 1053 comp_target_flush(ct); 1054 1055 comp_target_update_timings(ct); 1056 1057 if (r->acquired_buffer < 0) { 1058 // Ensures that renderings are created. 1059 renderer_acquire_swapchain_image(r); 1060 } 1061 1062 comp_target_update_timings(ct); 1063 1064 // Hardcoded for now. 1065 const uint32_t view_count = c->nr.view_count; 1066 enum comp_target_fov_source fov_source = COMP_TARGET_FOV_SOURCE_DISTORTION; 1067 1068 bool fast_path = c->base.frame_params.one_projection_layer_fast_path; 1069 bool do_timewarp = !c->debug.atw_off; 1070 1071 // Consistency check. 1072 assert(!fast_path || c->base.layer_accum.layer_count >= 1); 1073 1074 // For scratch image debugging. 1075 struct chl_frame_state frame_state; 1076 chl_frame_state_init( // 1077 &frame_state, // 1078 &c->nr, // 1079 view_count, // 1080 do_timewarp, // 1081 fast_path, // 1082 &c->scratch); // 1083 1084 bool use_compute = r->settings->use_compute; 1085 struct render_gfx render_g = {0}; 1086 struct render_compute render_c = {0}; 1087 1088 VkResult res = VK_SUCCESS; 1089 if (use_compute) { 1090 render_compute_init(&render_c, &c->nr); 1091 res = dispatch_compute(r, &render_c, &frame_state, fov_source); 1092 } else { 1093 render_gfx_init(&render_g, &c->nr); 1094 res = dispatch_graphics(r, &render_g, &frame_state, fov_source); 1095 } 1096 if (res != VK_SUCCESS) { 1097 return XRT_ERROR_VULKAN; 1098 } 1099 1100#ifdef XRT_FEATURE_WINDOW_PEEK 1101 if (c->peek) { 1102 switch (comp_window_peek_get_eye(c->peek)) { 1103 case COMP_WINDOW_PEEK_EYE_LEFT: { 1104 uint32_t scratch_index = frame_state.scratch_state.views[0].index; 1105 struct comp_scratch_single_images *view = &c->scratch.views[0].cssi; 1106 1107 comp_window_peek_blit( // 1108 c->peek, // 1109 view->images[scratch_index].image, // 1110 view->info.width, // 1111 view->info.height); // 1112 } break; 1113 case COMP_WINDOW_PEEK_EYE_RIGHT: { 1114 uint32_t scratch_index = frame_state.scratch_state.views[1].index; 1115 struct comp_scratch_single_images *view = &c->scratch.views[1].cssi; 1116 1117 comp_window_peek_blit( // 1118 c->peek, // 1119 view->images[scratch_index].image, // 1120 view->info.width, // 1121 view->info.height); // 1122 } break; 1123 case COMP_WINDOW_PEEK_EYE_BOTH: 1124 /* TODO: display the undistorted image */ 1125 comp_window_peek_blit(c->peek, c->target->images[r->acquired_buffer].handle, c->target->width, 1126 c->target->height); 1127 break; 1128 } 1129 } 1130#endif 1131 1132 renderer_present_swapchain_image(r, c->frame.rendering.desired_present_time_ns, 1133 c->frame.rendering.present_slop_ns); 1134 1135 // Save for timestamps below. 1136 uint64_t frame_id = c->frame.rendering.id; 1137 uint64_t desired_present_time_ns = c->frame.rendering.desired_present_time_ns; 1138 uint64_t predicted_display_time_ns = c->frame.rendering.predicted_display_time_ns; 1139 1140 // Clear the rendered frame. 1141 comp_frame_clear_locked(&c->frame.rendering); 1142 1143 xrt_result_t xret = XRT_SUCCESS; 1144 comp_mirror_fixup_ui_state(&r->mirror_to_debug_gui, c); 1145 if (comp_mirror_is_ready_and_active(&r->mirror_to_debug_gui, c, predicted_display_time_ns)) { 1146 1147 uint32_t scratch_index = frame_state.scratch_state.views[0].index; 1148 struct comp_scratch_single_images *view = &c->scratch.views[0].cssi; 1149 struct render_scratch_color_image *rsci = &view->images[scratch_index]; 1150 VkExtent2D extent = {view->info.width, view->info.width}; 1151 1152 // Used for both, want clamp to edge to no bring in black. 1153 VkSampler clamp_to_edge = c->nr.samplers.clamp_to_edge; 1154 1155 // Covers the whole view. 1156 struct xrt_normalized_rect rect = {0, 0, 1.0f, 1.0f}; 1157 1158 xret = comp_mirror_do_blit( // 1159 &r->mirror_to_debug_gui, // 1160 &c->base.vk, // 1161 frame_id, // 1162 predicted_display_time_ns, // 1163 rsci->image, // 1164 rsci->srgb_view, // 1165 clamp_to_edge, // 1166 extent, // 1167 rect); // 1168 } 1169 1170 /* 1171 * This fixes a lot of validation issues as it makes sure that the 1172 * command buffer has completed and all resources referred by it can 1173 * now be manipulated. 1174 * 1175 * This is done after a swap so isn't time critical. 1176 */ 1177 renderer_wait_queue_idle(r); 1178 1179 /* 1180 * Free any resources and finalize the scratch images, 1181 * which sends them send to debug UI if it is active. 1182 */ 1183 chl_frame_state_fini(&frame_state); 1184 1185 // Check timestamps. 1186 if (xret == XRT_SUCCESS) { 1187 /* 1188 * Get timestamps of GPU work (if available). 1189 */ 1190 1191 uint64_t gpu_start_ns, gpu_end_ns; 1192 if (render_resources_get_timestamps(&c->nr, &gpu_start_ns, &gpu_end_ns)) { 1193 uint64_t now_ns = os_monotonic_get_ns(); 1194 comp_target_info_gpu(ct, frame_id, gpu_start_ns, gpu_end_ns, now_ns); 1195 } 1196 } 1197 1198 1199 /* 1200 * Free resources. 1201 */ 1202 1203 if (use_compute) { 1204 render_compute_fini(&render_c); 1205 } else { 1206 render_gfx_fini(&render_g); 1207 } 1208 1209 renderer_wait_for_present(r, desired_present_time_ns); 1210 1211 comp_target_update_timings(ct); 1212 1213 return xret; 1214} 1215 1216struct comp_renderer * 1217comp_renderer_create(struct comp_compositor *c, VkExtent2D scratch_extent) 1218{ 1219 struct comp_renderer *r = U_TYPED_CALLOC(struct comp_renderer); 1220 1221 renderer_init(r, c, scratch_extent); 1222 1223 return r; 1224} 1225 1226void 1227comp_renderer_destroy(struct comp_renderer **ptr_r) 1228{ 1229 if (ptr_r == NULL) { 1230 return; 1231 } 1232 1233 struct comp_renderer *r = *ptr_r; 1234 if (r == NULL) { 1235 return; 1236 } 1237 1238 renderer_fini(r); 1239 1240 free(r); 1241 *ptr_r = NULL; 1242} 1243 1244void 1245comp_renderer_add_debug_vars(struct comp_renderer *self) 1246{ 1247 struct comp_renderer *r = self; 1248 1249 comp_mirror_add_debug_vars(&r->mirror_to_debug_gui, r->c); 1250}