The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

c/main: Wait for presentation using KHR_present_wait

Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2512>

+145 -38
+54 -29
src/xrt/compositor/main/comp_renderer.c
··· 831 831 } 832 832 833 833 static void 834 + renderer_wait_for_present(struct comp_renderer *r, uint64_t desired_present_time_ns) 835 + { 836 + struct comp_compositor *c = r->c; 837 + 838 + if (!comp_target_check_ready(c->target)) { 839 + return; 840 + } 841 + 842 + // For estimating frame misses. 843 + uint64_t before_ns = os_monotonic_get_ns(); 844 + 845 + if (c->target->wait_for_present_supported) { 846 + // reasonable timeout 847 + time_duration_ns timeout_ns = c->frame_interval_ns * 2.5f; 848 + 849 + // @note we don't actually care about the return value, just swallow errors, anything *critical* that 850 + // may be returned will be handled quite soon by later calls 851 + VkResult result = comp_target_wait_for_present(c->target, timeout_ns); 852 + (void)result; 853 + 854 + assert(result != VK_ERROR_EXTENSION_NOT_PRESENT); 855 + } else { 856 + /* 857 + * For direct mode this makes us wait until the last frame has been 858 + * actually shown to the user, this avoids us missing that we have 859 + * missed a frame and miss-predicting the next frame. 860 + * 861 + * Not all drivers follow this behaviour, so KHR_present_wait 862 + * should be preferred in all circumstances. 863 + * 864 + * Only do this if we are ready. 865 + */ 866 + 867 + // Do the acquire 868 + renderer_acquire_swapchain_image(r); 869 + } 870 + 871 + // How long did it take? 872 + uint64_t after_ns = os_monotonic_get_ns(); 873 + 874 + /* 875 + * Make sure we at least waited 1ms before warning. Then check 876 + * if we are more then 1ms behind when we wanted to present. 877 + */ 878 + if (before_ns + U_TIME_1MS_IN_NS < after_ns && // 879 + desired_present_time_ns + U_TIME_1MS_IN_NS < after_ns) { 880 + uint64_t diff_ns = after_ns - desired_present_time_ns; 881 + double diff_ms_f = time_ns_to_ms_f(diff_ns); 882 + COMP_WARN(c, "Compositor probably missed frame by %.2fms", diff_ms_f); 883 + } 884 + } 885 + 886 + static void 834 887 renderer_fini(struct comp_renderer *r) 835 888 { 836 889 struct vk_bundle *vk = &r->c->base.vk; ··· 1273 1326 render_gfx_fini(&render_g); 1274 1327 } 1275 1328 1276 - 1277 - /* 1278 - * For direct mode this makes us wait until the last frame has been 1279 - * actually shown to the user, this avoids us missing that we have 1280 - * missed a frame and miss-predicting the next frame. 1281 - * 1282 - * Only do this if we are ready. 1283 - */ 1284 - if (comp_target_check_ready(r->c->target)) { 1285 - // For estimating frame misses. 1286 - uint64_t then_ns = os_monotonic_get_ns(); 1287 - 1288 - // Do the acquire 1289 - renderer_acquire_swapchain_image(r); 1290 - 1291 - // How long did it take? 1292 - uint64_t now_ns = os_monotonic_get_ns(); 1293 - 1294 - /* 1295 - * Make sure we at least waited 1ms before warning. Then check 1296 - * if we are more then 1ms behind when we wanted to present. 1297 - */ 1298 - if (then_ns + U_TIME_1MS_IN_NS < now_ns && // 1299 - desired_present_time_ns + U_TIME_1MS_IN_NS < now_ns) { 1300 - uint64_t diff_ns = now_ns - desired_present_time_ns; 1301 - double diff_ms_f = time_ns_to_ms_f(diff_ns); 1302 - COMP_WARN(c, "Compositor probably missed frame by %.2fms", diff_ms_f); 1303 - } 1304 - } 1329 + renderer_wait_for_present(r, desired_present_time_ns); 1305 1330 1306 1331 comp_target_update_timings(ct); 1307 1332
+28 -1
src/xrt/compositor/main/comp_target.h
··· 154 154 //! Transformation of the current surface, required for pre-rotation 155 155 VkSurfaceTransformFlagBitsKHR surface_transform; 156 156 157 - // Holds semaphore information. 157 + //! Holds semaphore information. 158 158 struct comp_target_semaphores semaphores; 159 + 160 + //! Whether wait_for_present is supported by this comp_target. 161 + bool wait_for_present_supported; 159 162 160 163 /* 161 164 * ··· 227 230 uint64_t timeline_semaphore_value, 228 231 int64_t desired_present_time_ns, 229 232 int64_t present_slop_ns); 233 + 234 + /*! 235 + * Wait for the latest presented image to be displayed to the user. 236 + * 237 + * @param ct self 238 + * @param timeout_ns The amount of time to wait for presentation to succeed. 239 + */ 240 + VkResult (*wait_for_present)(struct comp_target *ct, time_duration_ns timeout_ns); 230 241 231 242 /*! 232 243 * Flush any WSI state before rendering. ··· 439 450 timeline_semaphore_value, // 440 451 desired_present_time_ns, // 441 452 present_slop_ns); // 453 + } 454 + 455 + /*! 456 + * @copydoc comp_target::wait_for_present 457 + * 458 + * @public @memberof comp_target 459 + * @ingroup comp_main 460 + */ 461 + static inline VkResult 462 + comp_target_wait_for_present(struct comp_target *ct, time_duration_ns timeout) 463 + { 464 + COMP_TRACE_MARKER(); 465 + 466 + return ct->wait_for_present( // 467 + ct, // 468 + timeout); // 442 469 } 443 470 444 471 /*!
+54 -8
src/xrt/compositor/main/comp_target_swapchain.c
··· 46 46 * to 3 anyways so we get what we want. 47 47 */ 48 48 DEBUG_GET_ONCE_NUM_OPTION(preferred_at_least_image_count, "XRT_COMPOSITOR_PREFERRED_IMAGE_COUNT", 2) 49 + DEBUG_GET_ONCE_BOOL_OPTION(use_present_wait, "XRT_COMPOSITOR_USE_PRESENT_WAIT", false) 49 50 50 51 static inline struct vk_bundle * 51 52 get_vk(struct comp_target_swapchain *cts) ··· 649 650 u_pc_fake_create(ct->c->frame_interval_ns, now_ns, &cts->upc); 650 651 } 651 652 653 + // if we have the present wait extension, mark it as supported now 654 + ct->wait_for_present_supported = vk->has_KHR_present_wait && debug_get_bool_option_use_present_wait(); 655 + 652 656 // Free old image views. 653 657 destroy_image_views(cts); 654 658 ··· 876 880 assert(cts->current_frame_id >= 0); 877 881 assert(cts->current_frame_id <= UINT32_MAX); 878 882 883 + VkPresentInfoKHR present_info = { 884 + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 885 + .pNext = NULL, 886 + .waitSemaphoreCount = 1, 887 + .pWaitSemaphores = &cts->base.semaphores.render_complete, 888 + .swapchainCount = 1, 889 + .pSwapchains = &cts->swapchain.handle, 890 + .pImageIndices = &index, 891 + }; 892 + 893 + #ifdef VK_GOOGLE_display_timing 879 894 VkPresentTimeGOOGLE times = { 880 895 .presentID = (uint32_t)cts->current_frame_id, 881 896 .desiredPresentTime = desired_present_time_ns - present_slop_ns, ··· 887 902 .pTimes = &times, 888 903 }; 889 904 890 - VkPresentInfoKHR presentInfo = { 891 - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, 892 - .pNext = vk->has_GOOGLE_display_timing ? &timings : NULL, 893 - .waitSemaphoreCount = 1, 894 - .pWaitSemaphores = &cts->base.semaphores.render_complete, 905 + if (vk->has_GOOGLE_display_timing) { 906 + vk_append_to_pnext_chain((VkBaseInStructure *)&present_info, (VkBaseInStructure *)&timings); 907 + } 908 + #endif 909 + 910 + #ifdef VK_KHR_present_id 911 + // @note first present should be 1, not 0, as the swapchain starts at ID 0 and increments from that 912 + uint64_t present_id = (uint64_t)cts->current_frame_id + 1; 913 + 914 + VkPresentIdKHR vk_present_id = { 915 + .sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR, 895 916 .swapchainCount = 1, 896 - .pSwapchains = &cts->swapchain.handle, 897 - .pImageIndices = &index, 917 + .pPresentIds = &present_id, 898 918 }; 919 + 920 + if (vk->features.present_wait) { 921 + vk_append_to_pnext_chain((VkBaseInStructure *)&present_info, (VkBaseInStructure *)&vk_present_id); 922 + } 923 + #endif 899 924 900 925 901 926 // Need to take the queue lock for present. 902 927 os_mutex_lock(&vk->queue_mutex); 903 - VkResult ret = vk->vkQueuePresentKHR(queue, &presentInfo); 928 + VkResult ret = vk->vkQueuePresentKHR(queue, &present_info); 904 929 os_mutex_unlock(&vk->queue_mutex); 905 930 906 931 ··· 916 941 #endif 917 942 918 943 return ret; 944 + } 945 + 946 + static VkResult 947 + comp_target_swapchain_wait_for_present(struct comp_target *ct, time_duration_ns timeout_ns) 948 + { 949 + struct comp_target_swapchain *cts = (struct comp_target_swapchain *)ct; 950 + struct vk_bundle *vk = get_vk(cts); 951 + 952 + #ifdef VK_KHR_present_wait 953 + if (!vk->features.present_wait) { 954 + return VK_ERROR_EXTENSION_NOT_PRESENT; 955 + } 956 + 957 + // @note current frame ID is incremented by 1 to match the ID given to Vulkan, see comp_target_swapchain_present 958 + return vk->vkWaitForPresentKHR(vk->device, cts->swapchain.handle, (uint64_t)cts->current_frame_id + 1, 959 + timeout_ns); 960 + #else 961 + return VK_ERROR_EXTENSION_NOT_PRESENT; 962 + #endif 919 963 } 920 964 921 965 static bool ··· 1091 1135 cts->base.has_images = comp_target_swapchain_has_images; 1092 1136 cts->base.acquire = comp_target_swapchain_acquire_next_image; 1093 1137 cts->base.present = comp_target_swapchain_present; 1138 + cts->base.wait_for_present = comp_target_swapchain_wait_for_present; 1094 1139 cts->base.calc_frame_pacing = comp_target_swapchain_calc_frame_pacing; 1095 1140 cts->base.mark_timing_point = comp_target_swapchain_mark_timing_point; 1096 1141 cts->base.update_timings = comp_target_swapchain_update_timings; 1097 1142 cts->base.info_gpu = comp_target_swapchain_info_gpu; 1143 + 1098 1144 os_thread_helper_init(&cts->vblank.event_thread); 1099 1145 }
+9
src/xrt/compositor/main/comp_window_debug_image.c
··· 212 212 return VK_SUCCESS; 213 213 } 214 214 215 + static VkResult 216 + target_wait_for_present(struct comp_target *ct, time_duration_ns timeout_ns) 217 + { 218 + return VK_ERROR_EXTENSION_NOT_PRESENT; 219 + } 220 + 215 221 static void 216 222 target_flush(struct comp_target *ct) 217 223 { ··· 340 346 dit->base.has_images = target_has_images; 341 347 dit->base.acquire = target_acquire; 342 348 dit->base.present = target_present; 349 + dit->base.wait_for_present = target_wait_for_present; 343 350 dit->base.flush = target_flush; 344 351 dit->base.calc_frame_pacing = target_calc_frame_pacing; 345 352 dit->base.mark_timing_point = target_mark_timing_point; ··· 348 355 dit->base.set_title = target_set_title; 349 356 dit->base.destroy = target_destroy; 350 357 dit->base.c = c; 358 + 359 + dit->base.wait_for_present_supported = false; 351 360 352 361 // Create the pacer. 353 362 uint64_t now_ns = os_monotonic_get_ns();