···1414#include <stdio.h>
1515#include <pthread.h>
16161717+struct u_sink_queue_elem
1818+{
1919+ struct xrt_frame *frame;
2020+ struct u_sink_queue_elem *next;
2121+};
17221823/*!
1924 * An @ref xrt_frame_sink queue, any frames received will be pushed to the
···3338 //! The consumer of the frames that are queued.
3439 struct xrt_frame_sink *consumer;
35403636- //! The current queued frame.
3737- struct xrt_frame *frame;
4141+ //! Front of the queue (oldest frame, first to be consumed)
4242+ struct u_sink_queue_elem *front;
4343+4444+ //! Back of the queue (newest frame, back->next is always null)
4545+ struct u_sink_queue_elem *back;
4646+4747+ //! Number of currently enqueued frames
4848+ uint64_t size;
4949+5050+ //! Max amount of frames before dropping new ones. 0 means unbounded.
5151+ uint64_t max_size;
38523953 pthread_t thread;
4054 pthread_mutex_t mutex;
4155 pthread_cond_t cond;
42564343- struct
4444- {
4545- uint64_t current;
4646- uint64_t last;
4747- } seq;
4848-4957 //! Should we keep running.
5058 bool running;
5159};
52606161+//! Call with q->mutex locked.
6262+static bool
6363+queue_is_empty(struct u_sink_queue *q)
6464+{
6565+ return q->size == 0;
6666+}
6767+6868+//! Call with q->mutex locked.
6969+static bool
7070+queue_is_full(struct u_sink_queue *q)
7171+{
7272+ bool is_unbounded = q->max_size == 0;
7373+ return q->size >= q->max_size && !is_unbounded;
7474+}
7575+7676+//! Pops the oldest frame, reference counting unchanged.
7777+//! Call with q->mutex locked.
7878+static struct xrt_frame *
7979+queue_pop(struct u_sink_queue *q)
8080+{
8181+ assert(!queue_is_empty(q));
8282+ struct xrt_frame *frame = q->front->frame;
8383+ struct u_sink_queue_elem *old_front = q->front;
8484+ q->front = q->front->next;
8585+ free(old_front);
8686+ q->size--;
8787+ if (q->front == NULL) {
8888+ assert(queue_is_empty(q));
8989+ q->back = NULL;
9090+ }
9191+ return frame;
9292+}
9393+9494+//! Tries to push a frame and increases its reference count.
9595+//! Call with q->mutex locked.
9696+static bool
9797+queue_try_refpush(struct u_sink_queue *q, struct xrt_frame *xf)
9898+{
9999+ if (!queue_is_full(q)) {
100100+ struct u_sink_queue_elem *elem = U_TYPED_CALLOC(struct u_sink_queue_elem);
101101+ xrt_frame_reference(&elem->frame, xf);
102102+ elem->next = NULL;
103103+ if (q->back == NULL) { // First frame
104104+ q->front = elem;
105105+ } else { // Next frame
106106+ q->back->next = elem;
107107+ }
108108+ q->back = elem;
109109+ q->size++;
110110+ return true;
111111+ }
112112+ return false;
113113+}
114114+115115+//! Clears the queue and unreferences all of its frames.
116116+//! Call with q->mutex locked.
117117+static void
118118+queue_refclear(struct u_sink_queue *q)
119119+{
120120+ while (!queue_is_empty(q)) {
121121+ assert((q->size > 1) ^ (q->front == q->back));
122122+ struct xrt_frame *xf = queue_pop(q);
123123+ xrt_frame_reference(&xf, NULL);
124124+ }
125125+}
126126+53127static void *
54128queue_mainloop(void *ptr)
55129{
···63137 while (q->running) {
6413865139 // No new frame, wait.
6666- if (q->seq.last >= q->seq.current) {
140140+ if (queue_is_empty(q)) {
67141 pthread_cond_wait(&q->cond, &q->mutex);
68142 }
69143···73147 }
7414875149 // Just in case.
7676- if (q->seq.last >= q->seq.current || q->frame == NULL) {
150150+ if (queue_is_empty(q)) {
77151 continue;
78152 }
7915380154 SINK_TRACE_IDENT(queue_frame);
811558282- // We have a new frame, send it out.
8383- q->seq.last = q->seq.current;
8484-85156 /*
157157+ * Dequeue frame.
86158 * We need to take a reference on the current frame, this is to
87159 * keep it alive during the call to the consumer should it be
88160 * replaced. But we no longer need to hold onto the frame on the
8989- * queue so we just move the pointer.
161161+ * queue so we just dequeue it.
90162 */
9191- frame = q->frame;
9292- q->frame = NULL;
163163+ frame = queue_pop(q);
9316494165 /*
95166 * Unlock the mutex when we do the work, so a new frame can be
···126197127198 // Only schedule new frames if we are running.
128199 if (q->running) {
129129- q->seq.current++;
130130- xrt_frame_reference(&q->frame, xf);
200200+ queue_try_refpush(q, xf);
131201 }
132202133203 // Wake up the thread.
···149219 q->running = false;
150220151221 // Release any frame waiting for submission.
152152- xrt_frame_reference(&q->frame, NULL);
222222+ queue_refclear(q);
153223154224 // Wake up the thread.
155225 pthread_cond_signal(&q->cond);
···180250 */
181251182252bool
183183-u_sink_queue_create(struct xrt_frame_context *xfctx, struct xrt_frame_sink *downstream, struct xrt_frame_sink **out_xfs)
253253+u_sink_queue_create(struct xrt_frame_context *xfctx,
254254+ uint64_t max_size,
255255+ struct xrt_frame_sink *downstream,
256256+ struct xrt_frame_sink **out_xfs)
184257{
185258 struct u_sink_queue *q = U_TYPED_CALLOC(struct u_sink_queue);
186259 int ret = 0;
···190263 q->node.destroy = queue_destroy;
191264 q->consumer = downstream;
192265 q->running = true;
266266+267267+ q->size = 0;
268268+ q->max_size = max_size;
193269194270 ret = pthread_mutex_init(&q->mutex, NULL);
195271 if (ret != 0) {
+2-2
src/xrt/drivers/ht/ht_driver.cpp
···714714715715 // This puts u_sink_create_to_r8g8b8_or_l8 on its own thread, so that nothing gets backed up if it runs slower
716716 // than the native camera framerate.
717717- u_sink_queue_create(&htd->camera.xfctx, tmp, &tmp);
717717+ u_sink_queue_create(&htd->camera.xfctx, 1, tmp, &tmp);
718718719719 // Converts images (we'd expect YUV422 or MJPEG) to R8G8B8. Can take a long time, especially on unoptimized
720720 // builds. If it's really slow, triple-check that you built Monado with optimizations!
···722722723723 // Puts the hand tracking code on its own thread, so that nothing upstream of it gets backed up if the hand
724724 // tracking code runs slower than the upstream framerate.
725725- u_sink_queue_create(&htd->camera.xfctx, tmp, &tmp);
725725+ u_sink_queue_create(&htd->camera.xfctx, 1, tmp, &tmp);
726726727727 xrt_fs_mode *modes;
728728 uint32_t count;