···120120121121/*
122122 *
123123+ * Wait helper thread.
124124+ *
125125+ */
126126+127127+static void
128128+wait_fence(struct xrt_compositor_fence **xcf_ptr)
129129+{
130130+ COMP_TRACE_MARKER();
131131+ xrt_result_t ret = XRT_SUCCESS;
132132+133133+ // 100ms
134134+ uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS;
135135+136136+ do {
137137+ ret = xrt_compositor_fence_wait(*xcf_ptr, timeout_ns);
138138+ if (ret != XRT_TIMEOUT) {
139139+ break;
140140+ }
141141+142142+ U_LOG_W("Waiting on client fence timed out > 100ms!");
143143+ } while (true);
144144+145145+ xrt_compositor_fence_destroy(xcf_ptr);
146146+147147+ if (ret != XRT_SUCCESS) {
148148+ U_LOG_E("Fence waiting failed!");
149149+ }
150150+}
151151+152152+static void
153153+wait_semaphore(struct xrt_compositor_semaphore **xcsem_ptr, uint64_t value)
154154+{
155155+ COMP_TRACE_MARKER();
156156+ xrt_result_t ret = XRT_SUCCESS;
157157+158158+ // 100ms
159159+ uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS;
160160+161161+ do {
162162+ ret = xrt_compositor_semaphore_wait(*xcsem_ptr, value, timeout_ns);
163163+ if (ret != XRT_TIMEOUT) {
164164+ break;
165165+ }
166166+167167+ U_LOG_W("Waiting on client semaphore value '%" PRIu64 "' timed out > 100ms!", value);
168168+ } while (true);
169169+170170+ xrt_compositor_semaphore_reference(xcsem_ptr, NULL);
171171+}
172172+173173+static void
174174+wait_for_scheduled_free(struct multi_compositor *mc)
175175+{
176176+ COMP_TRACE_MARKER();
177177+178178+ os_mutex_lock(&mc->slot_lock);
179179+180180+ // Block here if the scheduled slot is not clear.
181181+ while (mc->scheduled.active) {
182182+183183+ // Replace the scheduled frame if it's in the past.
184184+ uint64_t now_ns = os_monotonic_get_ns();
185185+ if (mc->scheduled.display_time_ns < now_ns) {
186186+ break;
187187+ }
188188+189189+ os_mutex_unlock(&mc->slot_lock);
190190+191191+ os_nanosleep(U_TIME_1MS_IN_NS);
192192+193193+ os_mutex_lock(&mc->slot_lock);
194194+ }
195195+196196+ slot_move_and_clear(&mc->scheduled, &mc->progress);
197197+198198+ os_mutex_unlock(&mc->slot_lock);
199199+}
200200+201201+static void *
202202+run_func(void *ptr)
203203+{
204204+ struct multi_compositor *mc = (struct multi_compositor *)ptr;
205205+206206+ os_thread_helper_lock(&mc->wait_thread.oth);
207207+208208+ while (os_thread_helper_is_running_locked(&mc->wait_thread.oth)) {
209209+210210+ if (mc->wait_thread.xcsem == NULL && mc->wait_thread.xcf == NULL) {
211211+ os_thread_helper_wait_locked(&mc->wait_thread.oth);
212212+ // Fall through here on stopping to clean up and outstanding waits.
213213+ }
214214+215215+ int64_t frame_id = mc->wait_thread.frame_id;
216216+ struct xrt_compositor_fence *xcf = mc->wait_thread.xcf;
217217+ struct xrt_compositor_semaphore *xcsem = mc->wait_thread.xcsem; // No need to ref, a move.
218218+ uint64_t value = mc->wait_thread.value;
219219+220220+ mc->wait_thread.frame_id = 0;
221221+ mc->wait_thread.xcf = NULL;
222222+ mc->wait_thread.xcsem = NULL;
223223+ mc->wait_thread.value = 0;
224224+225225+ // We are being stopped, loop back and check running.
226226+ if (xcf == NULL && xcsem == NULL) {
227227+ continue;
228228+ }
229229+230230+ // We now know that we should wait.
231231+ mc->wait_thread.waiting = true;
232232+233233+ os_thread_helper_unlock(&mc->wait_thread.oth);
234234+235235+ if (xcsem != NULL) {
236236+ wait_semaphore(&xcsem, value);
237237+ }
238238+ if (xcf != NULL) {
239239+ wait_fence(&xcf);
240240+ }
241241+242242+ // Sample time outside of lock.
243243+ uint64_t now_ns = os_monotonic_get_ns();
244244+245245+ os_mutex_lock(&mc->msc->list_and_timing_lock);
246246+ u_pa_mark_gpu_done(mc->upa, frame_id, now_ns);
247247+ os_mutex_unlock(&mc->msc->list_and_timing_lock);
248248+249249+ // Wait for the delivery slot.
250250+ wait_for_scheduled_free(mc);
251251+252252+ os_thread_helper_lock(&mc->wait_thread.oth);
253253+254254+ // Finally no longer waiting.
255255+ //! @todo Move to before wait_for_scheduled_free?
256256+ mc->wait_thread.waiting = false;
257257+258258+ if (mc->wait_thread.blocked) {
259259+ os_thread_helper_signal_locked(&mc->wait_thread.oth);
260260+ }
261261+ }
262262+263263+ os_thread_helper_unlock(&mc->wait_thread.oth);
264264+265265+ return NULL;
266266+}
267267+268268+static void
269269+wait_for_wait_thread_locked(struct multi_compositor *mc)
270270+{
271271+ // Should we wait for the last frame.
272272+ if (mc->wait_thread.waiting) {
273273+ COMP_TRACE_IDENT(blocked);
274274+ mc->wait_thread.blocked = true;
275275+ os_thread_helper_wait_locked(&mc->wait_thread.oth);
276276+ mc->wait_thread.blocked = false;
277277+ }
278278+}
279279+280280+static void
281281+wait_for_wait_thread(struct multi_compositor *mc)
282282+{
283283+ os_thread_helper_lock(&mc->wait_thread.oth);
284284+285285+ wait_for_wait_thread_locked(mc);
286286+287287+ os_thread_helper_unlock(&mc->wait_thread.oth);
288288+}
289289+290290+static void
291291+push_fence_to_wait_thread(struct multi_compositor *mc, int64_t frame_id, struct xrt_compositor_fence *xcf)
292292+{
293293+ os_thread_helper_lock(&mc->wait_thread.oth);
294294+295295+ // The function begin_layer should have waited, but just in case.
296296+ assert(!mc->wait_thread.waiting);
297297+ wait_for_wait_thread_locked(mc);
298298+299299+ assert(mc->wait_thread.xcf == NULL);
300300+301301+ mc->wait_thread.frame_id = frame_id;
302302+ mc->wait_thread.xcf = xcf;
303303+304304+ os_thread_helper_signal_locked(&mc->wait_thread.oth);
305305+306306+ os_thread_helper_unlock(&mc->wait_thread.oth);
307307+}
308308+309309+static void
310310+push_semaphore_to_wait_thread(struct multi_compositor *mc,
311311+ int64_t frame_id,
312312+ struct xrt_compositor_semaphore *xcsem,
313313+ uint64_t value)
314314+{
315315+ os_thread_helper_lock(&mc->wait_thread.oth);
316316+317317+ // The function begin_layer should have waited, but just in case.
318318+ assert(!mc->wait_thread.waiting);
319319+ wait_for_wait_thread_locked(mc);
320320+321321+ assert(mc->wait_thread.xcsem == NULL);
322322+323323+ mc->wait_thread.frame_id = frame_id;
324324+ xrt_compositor_semaphore_reference(&mc->wait_thread.xcsem, xcsem);
325325+ mc->wait_thread.value = value;
326326+327327+ os_thread_helper_signal_locked(&mc->wait_thread.oth);
328328+329329+ os_thread_helper_unlock(&mc->wait_thread.oth);
330330+}
331331+332332+333333+/*
334334+ *
123335 * Compositor functions.
124336 *
125337 */
···330542 u_pa_mark_delivered(mc->upa, frame_id, now_ns, display_time_ns);
331543 os_mutex_unlock(&mc->msc->list_and_timing_lock);
332544545545+ /*
546546+ * We have to block here for the waiting thread to push the last
547547+ * submitted frame from the progress slot to the scheduled slot,
548548+ * it only does after the sync object has signaled completion.
549549+ *
550550+ * If the previous frame's GPU work has not completed that means we
551551+ * will block here, but that is okay as the app has already submitted
552552+ * the GPU for this frame. This should have very little impact on GPU
553553+ * utilisation, if any.
554554+ */
555555+ wait_for_wait_thread(mc);
556556+333557 assert(mc->progress.layer_count == 0);
334558 U_ZERO(&mc->progress);
335559···461685 return XRT_SUCCESS;
462686}
463687464464-static void
465465-wait_fence(struct xrt_compositor_fence **xcf_ptr)
466466-{
467467- COMP_TRACE_MARKER();
468468- xrt_result_t ret = XRT_SUCCESS;
469469-470470- // 100ms
471471- uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS;
472472-473473- do {
474474- ret = xrt_compositor_fence_wait(*xcf_ptr, timeout_ns);
475475- if (ret != XRT_TIMEOUT) {
476476- break;
477477- }
478478-479479- U_LOG_W("Waiting on client fence timed out > 100ms!");
480480- } while (true);
481481-482482- xrt_compositor_fence_destroy(xcf_ptr);
483483-484484- if (ret != XRT_SUCCESS) {
485485- U_LOG_E("Fence waiting failed!");
486486- }
487487-}
488488-489489-static void
490490-wait_for_scheduled_free(struct multi_compositor *mc)
491491-{
492492- COMP_TRACE_MARKER();
493493-494494- os_mutex_lock(&mc->slot_lock);
495495-496496- // Block here if the scheduled slot is not clear.
497497- while (mc->scheduled.active) {
498498-499499- // Replace the scheduled frame if it's in the past.
500500- uint64_t now_ns = os_monotonic_get_ns();
501501- if (mc->scheduled.display_time_ns < now_ns) {
502502- break;
503503- }
504504-505505- os_mutex_unlock(&mc->slot_lock);
506506-507507- os_nanosleep(U_TIME_1MS_IN_NS);
508508-509509- os_mutex_lock(&mc->slot_lock);
510510- }
511511-512512- slot_move_and_clear(&mc->scheduled, &mc->progress);
513513-514514- os_mutex_unlock(&mc->slot_lock);
515515-}
516516-517688static xrt_result_t
518689multi_compositor_layer_commit(struct xrt_compositor *xc, int64_t frame_id, xrt_graphics_sync_handle_t sync_handle)
519690{
···544715 } while (false); // Goto without the labels.
545716546717 if (xcf != NULL) {
547547- wait_fence(&xcf);
548548- }
718718+ push_fence_to_wait_thread(mc, frame_id, xcf);
719719+ } else {
720720+ // Assume that the app side compositor waited.
721721+ uint64_t now_ns = os_monotonic_get_ns();
549722550550- wait_for_scheduled_free(mc);
551551- uint64_t now_ns = os_monotonic_get_ns();
723723+ os_mutex_lock(&mc->msc->list_and_timing_lock);
724724+ u_pa_mark_gpu_done(mc->upa, frame_id, now_ns);
725725+ os_mutex_unlock(&mc->msc->list_and_timing_lock);
552726553553- os_mutex_lock(&mc->msc->list_and_timing_lock);
554554- u_pa_mark_gpu_done(mc->upa, frame_id, now_ns);
555555- os_mutex_unlock(&mc->msc->list_and_timing_lock);
727727+ wait_for_scheduled_free(mc);
728728+ }
556729557730 return XRT_SUCCESS;
558731}
559732560560-static void
561561-wait_semaphore(struct xrt_compositor_semaphore *xcsem, uint64_t value)
562562-{
563563- COMP_TRACE_MARKER();
564564- xrt_result_t ret = XRT_SUCCESS;
565565-566566- // 100ms
567567- uint64_t timeout_ns = 100 * U_TIME_1MS_IN_NS;
568568-569569- do {
570570- ret = xrt_compositor_semaphore_wait(xcsem, value, timeout_ns);
571571- if (ret != XRT_TIMEOUT) {
572572- break;
573573- }
574574-575575- U_LOG_W("Waiting on client semaphore value '%" PRIu64 "' timed out > 100ms!", value);
576576- } while (true);
577577-}
578578-579733static xrt_result_t
580734multi_compositor_layer_commit_with_semaphore(struct xrt_compositor *xc,
581735 int64_t frame_id,
···586740587741 struct multi_compositor *mc = multi_compositor(xc);
588742589589- // Wait for the semaphore.
590590- wait_semaphore(xcsem, value);
591591-592592- wait_for_scheduled_free(mc);
593593- uint64_t now_ns = os_monotonic_get_ns();
594594-595595- os_mutex_lock(&mc->msc->list_and_timing_lock);
596596- u_pa_mark_gpu_done(mc->upa, frame_id, now_ns);
597597- os_mutex_unlock(&mc->msc->list_and_timing_lock);
743743+ push_semaphore_to_wait_thread(mc, frame_id, xcsem, value);
598744599745 return XRT_SUCCESS;
600746}
···630776 os_mutex_unlock(&mc->msc->list_and_timing_lock);
631777632778 drain_events(mc);
779779+780780+ // Stop the wait thread.
781781+ os_thread_helper_stop(&mc->wait_thread.oth);
633782634783 // We are now off the rendering list, clear slots for any swapchains.
635784 slot_clear(&mc->progress);
···701850702851 os_mutex_init(&mc->event.mutex);
703852 os_mutex_init(&mc->slot_lock);
853853+ os_thread_helper_init(&mc->wait_thread.oth);
704854705855 // Passthrough our formats from the native compositor to the client.
706856 mc->base.base.info = msc->xcn->base.info;
···729879 msc->last_timings.diff_ns); //
730880731881 os_mutex_unlock(&msc->list_and_timing_lock);
882882+883883+ // Last start the wait thread.
884884+ os_thread_helper_start(&mc->wait_thread.oth, run_func, mc);
732885733886 *out_xcn = &mc->base;
734887
+24
src/xrt/compositor/multi/comp_multi_private.h
···126126 int64_t z_order;
127127 } state;
128128129129+ struct
130130+ {
131131+ //! Fence to wait for.
132132+ struct xrt_compositor_fence *xcf;
133133+134134+ //! Timeline semaphore to wait for.
135135+ struct xrt_compositor_semaphore *xcsem;
136136+137137+ //! Timeline semaphore value to wait for.
138138+ uint64_t value;
139139+140140+ //! Frame id of frame being waited on.
141141+ int64_t frame_id;
142142+143143+ //! The wait thread itself
144144+ struct os_thread_helper oth;
145145+146146+ //! Is the thread waiting, if so the client should block.
147147+ bool waiting;
148148+149149+ //! Is the client thread blocked, if so it should be woken up.
150150+ bool blocked;
151151+ } wait_thread;
152152+129153 //! Lock for all of the slots.
130154 struct os_mutex slot_lock;
131155