···831831}
832832833833static void
834834+renderer_wait_for_present(struct comp_renderer *r, uint64_t desired_present_time_ns)
835835+{
836836+ struct comp_compositor *c = r->c;
837837+838838+ if (!comp_target_check_ready(c->target)) {
839839+ return;
840840+ }
841841+842842+ // For estimating frame misses.
843843+ uint64_t before_ns = os_monotonic_get_ns();
844844+845845+ if (c->target->wait_for_present_supported) {
846846+ // reasonable timeout
847847+ time_duration_ns timeout_ns = c->frame_interval_ns * 2.5f;
848848+849849+ // @note we don't actually care about the return value, just swallow errors, anything *critical* that
850850+ // may be returned will be handled quite soon by later calls
851851+ VkResult result = comp_target_wait_for_present(c->target, timeout_ns);
852852+ (void)result;
853853+854854+ assert(result != VK_ERROR_EXTENSION_NOT_PRESENT);
855855+ } else {
856856+ /*
857857+ * For direct mode this makes us wait until the last frame has been
858858+ * actually shown to the user, this avoids us missing that we have
859859+ * missed a frame and miss-predicting the next frame.
860860+ *
861861+ * Not all drivers follow this behaviour, so KHR_present_wait
862862+ * should be preferred in all circumstances.
863863+ *
864864+ * Only do this if we are ready.
865865+ */
866866+867867+ // Do the acquire
868868+ renderer_acquire_swapchain_image(r);
869869+ }
870870+871871+ // How long did it take?
872872+ uint64_t after_ns = os_monotonic_get_ns();
873873+874874+ /*
875875+ * Make sure we at least waited 1ms before warning. Then check
876876+ * if we are more then 1ms behind when we wanted to present.
877877+ */
878878+ if (before_ns + U_TIME_1MS_IN_NS < after_ns && //
879879+ desired_present_time_ns + U_TIME_1MS_IN_NS < after_ns) {
880880+ uint64_t diff_ns = after_ns - desired_present_time_ns;
881881+ double diff_ms_f = time_ns_to_ms_f(diff_ns);
882882+ COMP_WARN(c, "Compositor probably missed frame by %.2fms", diff_ms_f);
883883+ }
884884+}
885885+886886+static void
834887renderer_fini(struct comp_renderer *r)
835888{
836889 struct vk_bundle *vk = &r->c->base.vk;
···12731326 render_gfx_fini(&render_g);
12741327 }
1275132812761276-12771277- /*
12781278- * For direct mode this makes us wait until the last frame has been
12791279- * actually shown to the user, this avoids us missing that we have
12801280- * missed a frame and miss-predicting the next frame.
12811281- *
12821282- * Only do this if we are ready.
12831283- */
12841284- if (comp_target_check_ready(r->c->target)) {
12851285- // For estimating frame misses.
12861286- uint64_t then_ns = os_monotonic_get_ns();
12871287-12881288- // Do the acquire
12891289- renderer_acquire_swapchain_image(r);
12901290-12911291- // How long did it take?
12921292- uint64_t now_ns = os_monotonic_get_ns();
12931293-12941294- /*
12951295- * Make sure we at least waited 1ms before warning. Then check
12961296- * if we are more then 1ms behind when we wanted to present.
12971297- */
12981298- if (then_ns + U_TIME_1MS_IN_NS < now_ns && //
12991299- desired_present_time_ns + U_TIME_1MS_IN_NS < now_ns) {
13001300- uint64_t diff_ns = now_ns - desired_present_time_ns;
13011301- double diff_ms_f = time_ns_to_ms_f(diff_ns);
13021302- COMP_WARN(c, "Compositor probably missed frame by %.2fms", diff_ms_f);
13031303- }
13041304- }
13291329+ renderer_wait_for_present(r, desired_present_time_ns);
1305133013061331 comp_target_update_timings(ct);
13071332
+28-1
src/xrt/compositor/main/comp_target.h
···154154 //! Transformation of the current surface, required for pre-rotation
155155 VkSurfaceTransformFlagBitsKHR surface_transform;
156156157157- // Holds semaphore information.
157157+ //! Holds semaphore information.
158158 struct comp_target_semaphores semaphores;
159159+160160+ //! Whether wait_for_present is supported by this comp_target.
161161+ bool wait_for_present_supported;
159162160163 /*
161164 *
···227230 uint64_t timeline_semaphore_value,
228231 int64_t desired_present_time_ns,
229232 int64_t present_slop_ns);
233233+234234+ /*!
235235+ * Wait for the latest presented image to be displayed to the user.
236236+ *
237237+ * @param ct self
238238+ * @param timeout_ns The amount of time to wait for presentation to succeed.
239239+ */
240240+ VkResult (*wait_for_present)(struct comp_target *ct, time_duration_ns timeout_ns);
230241231242 /*!
232243 * Flush any WSI state before rendering.
···439450 timeline_semaphore_value, //
440451 desired_present_time_ns, //
441452 present_slop_ns); //
453453+}
454454+455455+/*!
456456+ * @copydoc comp_target::wait_for_present
457457+ *
458458+ * @public @memberof comp_target
459459+ * @ingroup comp_main
460460+ */
461461+static inline VkResult
462462+comp_target_wait_for_present(struct comp_target *ct, time_duration_ns timeout)
463463+{
464464+ COMP_TRACE_MARKER();
465465+466466+ return ct->wait_for_present( //
467467+ ct, //
468468+ timeout); //
442469}
443470444471/*!
+54-8
src/xrt/compositor/main/comp_target_swapchain.c
···4646 * to 3 anyways so we get what we want.
4747 */
4848DEBUG_GET_ONCE_NUM_OPTION(preferred_at_least_image_count, "XRT_COMPOSITOR_PREFERRED_IMAGE_COUNT", 2)
4949+DEBUG_GET_ONCE_BOOL_OPTION(use_present_wait, "XRT_COMPOSITOR_USE_PRESENT_WAIT", false)
49505051static inline struct vk_bundle *
5152get_vk(struct comp_target_swapchain *cts)
···649650 u_pc_fake_create(ct->c->frame_interval_ns, now_ns, &cts->upc);
650651 }
651652653653+ // if we have the present wait extension, mark it as supported now
654654+ ct->wait_for_present_supported = vk->has_KHR_present_wait && debug_get_bool_option_use_present_wait();
655655+652656 // Free old image views.
653657 destroy_image_views(cts);
654658···876880 assert(cts->current_frame_id >= 0);
877881 assert(cts->current_frame_id <= UINT32_MAX);
878882883883+ VkPresentInfoKHR present_info = {
884884+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
885885+ .pNext = NULL,
886886+ .waitSemaphoreCount = 1,
887887+ .pWaitSemaphores = &cts->base.semaphores.render_complete,
888888+ .swapchainCount = 1,
889889+ .pSwapchains = &cts->swapchain.handle,
890890+ .pImageIndices = &index,
891891+ };
892892+893893+#ifdef VK_GOOGLE_display_timing
879894 VkPresentTimeGOOGLE times = {
880895 .presentID = (uint32_t)cts->current_frame_id,
881896 .desiredPresentTime = desired_present_time_ns - present_slop_ns,
···887902 .pTimes = ×,
888903 };
889904890890- VkPresentInfoKHR presentInfo = {
891891- .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
892892- .pNext = vk->has_GOOGLE_display_timing ? &timings : NULL,
893893- .waitSemaphoreCount = 1,
894894- .pWaitSemaphores = &cts->base.semaphores.render_complete,
905905+ if (vk->has_GOOGLE_display_timing) {
906906+ vk_append_to_pnext_chain((VkBaseInStructure *)&present_info, (VkBaseInStructure *)&timings);
907907+ }
908908+#endif
909909+910910+#ifdef VK_KHR_present_id
911911+ // @note first present should be 1, not 0, as the swapchain starts at ID 0 and increments from that
912912+ uint64_t present_id = (uint64_t)cts->current_frame_id + 1;
913913+914914+ VkPresentIdKHR vk_present_id = {
915915+ .sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR,
895916 .swapchainCount = 1,
896896- .pSwapchains = &cts->swapchain.handle,
897897- .pImageIndices = &index,
917917+ .pPresentIds = &present_id,
898918 };
919919+920920+ if (vk->features.present_wait) {
921921+ vk_append_to_pnext_chain((VkBaseInStructure *)&present_info, (VkBaseInStructure *)&vk_present_id);
922922+ }
923923+#endif
899924900925901926 // Need to take the queue lock for present.
902927 os_mutex_lock(&vk->queue_mutex);
903903- VkResult ret = vk->vkQueuePresentKHR(queue, &presentInfo);
928928+ VkResult ret = vk->vkQueuePresentKHR(queue, &present_info);
904929 os_mutex_unlock(&vk->queue_mutex);
905930906931···916941#endif
917942918943 return ret;
944944+}
945945+946946+static VkResult
947947+comp_target_swapchain_wait_for_present(struct comp_target *ct, time_duration_ns timeout_ns)
948948+{
949949+ struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct;
950950+ struct vk_bundle *vk = get_vk(cts);
951951+952952+#ifdef VK_KHR_present_wait
953953+ if (!vk->features.present_wait) {
954954+ return VK_ERROR_EXTENSION_NOT_PRESENT;
955955+ }
956956+957957+ // @note current frame ID is incremented by 1 to match the ID given to Vulkan, see comp_target_swapchain_present
958958+ return vk->vkWaitForPresentKHR(vk->device, cts->swapchain.handle, (uint64_t)cts->current_frame_id + 1,
959959+ timeout_ns);
960960+#else
961961+ return VK_ERROR_EXTENSION_NOT_PRESENT;
962962+#endif
919963}
920964921965static bool
···10911135 cts->base.has_images = comp_target_swapchain_has_images;
10921136 cts->base.acquire = comp_target_swapchain_acquire_next_image;
10931137 cts->base.present = comp_target_swapchain_present;
11381138+ cts->base.wait_for_present = comp_target_swapchain_wait_for_present;
10941139 cts->base.calc_frame_pacing = comp_target_swapchain_calc_frame_pacing;
10951140 cts->base.mark_timing_point = comp_target_swapchain_mark_timing_point;
10961141 cts->base.update_timings = comp_target_swapchain_update_timings;
10971142 cts->base.info_gpu = comp_target_swapchain_info_gpu;
11431143+10981144 os_thread_helper_init(&cts->vblank.event_thread);
10991145}