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