The open source OpenXR runtime
0
fork

Configure Feed

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

c/multi: Add per client thread that waits for sync objects

+265 -88
+241 -88
src/xrt/compositor/multi/comp_multi_compositor.c
··· 120 120 121 121 /* 122 122 * 123 + * Wait helper thread. 124 + * 125 + */ 126 + 127 + static void 128 + wait_fence(struct xrt_compositor_fence **xcf_ptr) 129 + { 130 + COMP_TRACE_MARKER(); 131 + xrt_result_t ret = XRT_SUCCESS; 132 + 133 + // 100ms 134 + uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS; 135 + 136 + do { 137 + ret = xrt_compositor_fence_wait(*xcf_ptr, timeout_ns); 138 + if (ret != XRT_TIMEOUT) { 139 + break; 140 + } 141 + 142 + U_LOG_W("Waiting on client fence timed out > 100ms!"); 143 + } while (true); 144 + 145 + xrt_compositor_fence_destroy(xcf_ptr); 146 + 147 + if (ret != XRT_SUCCESS) { 148 + U_LOG_E("Fence waiting failed!"); 149 + } 150 + } 151 + 152 + static void 153 + wait_semaphore(struct xrt_compositor_semaphore **xcsem_ptr, uint64_t value) 154 + { 155 + COMP_TRACE_MARKER(); 156 + xrt_result_t ret = XRT_SUCCESS; 157 + 158 + // 100ms 159 + uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS; 160 + 161 + do { 162 + ret = xrt_compositor_semaphore_wait(*xcsem_ptr, value, timeout_ns); 163 + if (ret != XRT_TIMEOUT) { 164 + break; 165 + } 166 + 167 + U_LOG_W("Waiting on client semaphore value '%" PRIu64 "' timed out > 100ms!", value); 168 + } while (true); 169 + 170 + xrt_compositor_semaphore_reference(xcsem_ptr, NULL); 171 + } 172 + 173 + static void 174 + wait_for_scheduled_free(struct multi_compositor *mc) 175 + { 176 + COMP_TRACE_MARKER(); 177 + 178 + os_mutex_lock(&mc->slot_lock); 179 + 180 + // Block here if the scheduled slot is not clear. 181 + while (mc->scheduled.active) { 182 + 183 + // Replace the scheduled frame if it's in the past. 184 + uint64_t now_ns = os_monotonic_get_ns(); 185 + if (mc->scheduled.display_time_ns < now_ns) { 186 + break; 187 + } 188 + 189 + os_mutex_unlock(&mc->slot_lock); 190 + 191 + os_nanosleep(U_TIME_1MS_IN_NS); 192 + 193 + os_mutex_lock(&mc->slot_lock); 194 + } 195 + 196 + slot_move_and_clear(&mc->scheduled, &mc->progress); 197 + 198 + os_mutex_unlock(&mc->slot_lock); 199 + } 200 + 201 + static void * 202 + run_func(void *ptr) 203 + { 204 + struct multi_compositor *mc = (struct multi_compositor *)ptr; 205 + 206 + os_thread_helper_lock(&mc->wait_thread.oth); 207 + 208 + while (os_thread_helper_is_running_locked(&mc->wait_thread.oth)) { 209 + 210 + if (mc->wait_thread.xcsem == NULL && mc->wait_thread.xcf == NULL) { 211 + os_thread_helper_wait_locked(&mc->wait_thread.oth); 212 + // Fall through here on stopping to clean up and outstanding waits. 213 + } 214 + 215 + int64_t frame_id = mc->wait_thread.frame_id; 216 + struct xrt_compositor_fence *xcf = mc->wait_thread.xcf; 217 + struct xrt_compositor_semaphore *xcsem = mc->wait_thread.xcsem; // No need to ref, a move. 218 + uint64_t value = mc->wait_thread.value; 219 + 220 + mc->wait_thread.frame_id = 0; 221 + mc->wait_thread.xcf = NULL; 222 + mc->wait_thread.xcsem = NULL; 223 + mc->wait_thread.value = 0; 224 + 225 + // We are being stopped, loop back and check running. 226 + if (xcf == NULL && xcsem == NULL) { 227 + continue; 228 + } 229 + 230 + // We now know that we should wait. 231 + mc->wait_thread.waiting = true; 232 + 233 + os_thread_helper_unlock(&mc->wait_thread.oth); 234 + 235 + if (xcsem != NULL) { 236 + wait_semaphore(&xcsem, value); 237 + } 238 + if (xcf != NULL) { 239 + wait_fence(&xcf); 240 + } 241 + 242 + // Sample time outside of lock. 243 + uint64_t now_ns = os_monotonic_get_ns(); 244 + 245 + os_mutex_lock(&mc->msc->list_and_timing_lock); 246 + u_pa_mark_gpu_done(mc->upa, frame_id, now_ns); 247 + os_mutex_unlock(&mc->msc->list_and_timing_lock); 248 + 249 + // Wait for the delivery slot. 250 + wait_for_scheduled_free(mc); 251 + 252 + os_thread_helper_lock(&mc->wait_thread.oth); 253 + 254 + // Finally no longer waiting. 255 + //! @todo Move to before wait_for_scheduled_free? 256 + mc->wait_thread.waiting = false; 257 + 258 + if (mc->wait_thread.blocked) { 259 + os_thread_helper_signal_locked(&mc->wait_thread.oth); 260 + } 261 + } 262 + 263 + os_thread_helper_unlock(&mc->wait_thread.oth); 264 + 265 + return NULL; 266 + } 267 + 268 + static void 269 + wait_for_wait_thread_locked(struct multi_compositor *mc) 270 + { 271 + // Should we wait for the last frame. 272 + if (mc->wait_thread.waiting) { 273 + COMP_TRACE_IDENT(blocked); 274 + mc->wait_thread.blocked = true; 275 + os_thread_helper_wait_locked(&mc->wait_thread.oth); 276 + mc->wait_thread.blocked = false; 277 + } 278 + } 279 + 280 + static void 281 + wait_for_wait_thread(struct multi_compositor *mc) 282 + { 283 + os_thread_helper_lock(&mc->wait_thread.oth); 284 + 285 + wait_for_wait_thread_locked(mc); 286 + 287 + os_thread_helper_unlock(&mc->wait_thread.oth); 288 + } 289 + 290 + static void 291 + push_fence_to_wait_thread(struct multi_compositor *mc, int64_t frame_id, struct xrt_compositor_fence *xcf) 292 + { 293 + os_thread_helper_lock(&mc->wait_thread.oth); 294 + 295 + // The function begin_layer should have waited, but just in case. 296 + assert(!mc->wait_thread.waiting); 297 + wait_for_wait_thread_locked(mc); 298 + 299 + assert(mc->wait_thread.xcf == NULL); 300 + 301 + mc->wait_thread.frame_id = frame_id; 302 + mc->wait_thread.xcf = xcf; 303 + 304 + os_thread_helper_signal_locked(&mc->wait_thread.oth); 305 + 306 + os_thread_helper_unlock(&mc->wait_thread.oth); 307 + } 308 + 309 + static void 310 + push_semaphore_to_wait_thread(struct multi_compositor *mc, 311 + int64_t frame_id, 312 + struct xrt_compositor_semaphore *xcsem, 313 + uint64_t value) 314 + { 315 + os_thread_helper_lock(&mc->wait_thread.oth); 316 + 317 + // The function begin_layer should have waited, but just in case. 318 + assert(!mc->wait_thread.waiting); 319 + wait_for_wait_thread_locked(mc); 320 + 321 + assert(mc->wait_thread.xcsem == NULL); 322 + 323 + mc->wait_thread.frame_id = frame_id; 324 + xrt_compositor_semaphore_reference(&mc->wait_thread.xcsem, xcsem); 325 + mc->wait_thread.value = value; 326 + 327 + os_thread_helper_signal_locked(&mc->wait_thread.oth); 328 + 329 + os_thread_helper_unlock(&mc->wait_thread.oth); 330 + } 331 + 332 + 333 + /* 334 + * 123 335 * Compositor functions. 124 336 * 125 337 */ ··· 330 542 u_pa_mark_delivered(mc->upa, frame_id, now_ns, display_time_ns); 331 543 os_mutex_unlock(&mc->msc->list_and_timing_lock); 332 544 545 + /* 546 + * We have to block here for the waiting thread to push the last 547 + * submitted frame from the progress slot to the scheduled slot, 548 + * it only does after the sync object has signaled completion. 549 + * 550 + * If the previous frame's GPU work has not completed that means we 551 + * will block here, but that is okay as the app has already submitted 552 + * the GPU for this frame. This should have very little impact on GPU 553 + * utilisation, if any. 554 + */ 555 + wait_for_wait_thread(mc); 556 + 333 557 assert(mc->progress.layer_count == 0); 334 558 U_ZERO(&mc->progress); 335 559 ··· 461 685 return XRT_SUCCESS; 462 686 } 463 687 464 - static void 465 - wait_fence(struct xrt_compositor_fence **xcf_ptr) 466 - { 467 - COMP_TRACE_MARKER(); 468 - xrt_result_t ret = XRT_SUCCESS; 469 - 470 - // 100ms 471 - uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS; 472 - 473 - do { 474 - ret = xrt_compositor_fence_wait(*xcf_ptr, timeout_ns); 475 - if (ret != XRT_TIMEOUT) { 476 - break; 477 - } 478 - 479 - U_LOG_W("Waiting on client fence timed out > 100ms!"); 480 - } while (true); 481 - 482 - xrt_compositor_fence_destroy(xcf_ptr); 483 - 484 - if (ret != XRT_SUCCESS) { 485 - U_LOG_E("Fence waiting failed!"); 486 - } 487 - } 488 - 489 - static void 490 - wait_for_scheduled_free(struct multi_compositor *mc) 491 - { 492 - COMP_TRACE_MARKER(); 493 - 494 - os_mutex_lock(&mc->slot_lock); 495 - 496 - // Block here if the scheduled slot is not clear. 497 - while (mc->scheduled.active) { 498 - 499 - // Replace the scheduled frame if it's in the past. 500 - uint64_t now_ns = os_monotonic_get_ns(); 501 - if (mc->scheduled.display_time_ns < now_ns) { 502 - break; 503 - } 504 - 505 - os_mutex_unlock(&mc->slot_lock); 506 - 507 - os_nanosleep(U_TIME_1MS_IN_NS); 508 - 509 - os_mutex_lock(&mc->slot_lock); 510 - } 511 - 512 - slot_move_and_clear(&mc->scheduled, &mc->progress); 513 - 514 - os_mutex_unlock(&mc->slot_lock); 515 - } 516 - 517 688 static xrt_result_t 518 689 multi_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle) 519 690 { ··· 544 715 } while (false); // Goto without the labels. 545 716 546 717 if (xcf != NULL) { 547 - wait_fence(&xcf); 548 - } 718 + push_fence_to_wait_thread(mc, frame_id, xcf); 719 + } else { 720 + // Assume that the app side compositor waited. 721 + uint64_t now_ns = os_monotonic_get_ns(); 549 722 550 - wait_for_scheduled_free(mc); 551 - uint64_t now_ns = os_monotonic_get_ns(); 723 + os_mutex_lock(&mc->msc->list_and_timing_lock); 724 + u_pa_mark_gpu_done(mc->upa, frame_id, now_ns); 725 + os_mutex_unlock(&mc->msc->list_and_timing_lock); 552 726 553 - os_mutex_lock(&mc->msc->list_and_timing_lock); 554 - u_pa_mark_gpu_done(mc->upa, frame_id, now_ns); 555 - os_mutex_unlock(&mc->msc->list_and_timing_lock); 727 + wait_for_scheduled_free(mc); 728 + } 556 729 557 730 return XRT_SUCCESS; 558 731 } 559 732 560 - static void 561 - wait_semaphore(struct xrt_compositor_semaphore *xcsem, uint64_t value) 562 - { 563 - COMP_TRACE_MARKER(); 564 - xrt_result_t ret = XRT_SUCCESS; 565 - 566 - // 100ms 567 - uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS; 568 - 569 - do { 570 - ret = xrt_compositor_semaphore_wait(xcsem, value, timeout_ns); 571 - if (ret != XRT_TIMEOUT) { 572 - break; 573 - } 574 - 575 - U_LOG_W("Waiting on client semaphore value '%" PRIu64 "' timed out > 100ms!", value); 576 - } while (true); 577 - } 578 - 579 733 static xrt_result_t 580 734 multi_compositor_layer_commit_with_semaphore(struct xrt_compositor *xc, 581 735 int64_t frame_id, ··· 586 740 587 741 struct multi_compositor *mc = multi_compositor(xc); 588 742 589 - // Wait for the semaphore. 590 - wait_semaphore(xcsem, value); 591 - 592 - wait_for_scheduled_free(mc); 593 - uint64_t now_ns = os_monotonic_get_ns(); 594 - 595 - os_mutex_lock(&mc->msc->list_and_timing_lock); 596 - u_pa_mark_gpu_done(mc->upa, frame_id, now_ns); 597 - os_mutex_unlock(&mc->msc->list_and_timing_lock); 743 + push_semaphore_to_wait_thread(mc, frame_id, xcsem, value); 598 744 599 745 return XRT_SUCCESS; 600 746 } ··· 630 776 os_mutex_unlock(&mc->msc->list_and_timing_lock); 631 777 632 778 drain_events(mc); 779 + 780 + // Stop the wait thread. 781 + os_thread_helper_stop(&mc->wait_thread.oth); 633 782 634 783 // We are now off the rendering list, clear slots for any swapchains. 635 784 slot_clear(&mc->progress); ··· 701 850 702 851 os_mutex_init(&mc->event.mutex); 703 852 os_mutex_init(&mc->slot_lock); 853 + os_thread_helper_init(&mc->wait_thread.oth); 704 854 705 855 // Passthrough our formats from the native compositor to the client. 706 856 mc->base.base.info = msc->xcn->base.info; ··· 729 879 msc->last_timings.diff_ns); // 730 880 731 881 os_mutex_unlock(&msc->list_and_timing_lock); 882 + 883 + // Last start the wait thread. 884 + os_thread_helper_start(&mc->wait_thread.oth, run_func, mc); 732 885 733 886 *out_xcn = &mc->base; 734 887
+24
src/xrt/compositor/multi/comp_multi_private.h
··· 126 126 int64_t z_order; 127 127 } state; 128 128 129 + struct 130 + { 131 + //! Fence to wait for. 132 + struct xrt_compositor_fence *xcf; 133 + 134 + //! Timeline semaphore to wait for. 135 + struct xrt_compositor_semaphore *xcsem; 136 + 137 + //! Timeline semaphore value to wait for. 138 + uint64_t value; 139 + 140 + //! Frame id of frame being waited on. 141 + int64_t frame_id; 142 + 143 + //! The wait thread itself 144 + struct os_thread_helper oth; 145 + 146 + //! Is the thread waiting, if so the client should block. 147 + bool waiting; 148 + 149 + //! Is the client thread blocked, if so it should be woken up. 150 + bool blocked; 151 + } wait_thread; 152 + 129 153 //! Lock for all of the slots. 130 154 struct os_mutex slot_lock; 131 155