···12121313#include "util/u_misc.h"
1414#include "util/u_handles.h"
1515+#include "util/u_trace_marker.h"
15161617#include "util/comp_swapchain.h"
1718#include "vk/vk_cmd_pool.h"
···1920#include <stdio.h>
2021#include <stdlib.h>
2122#include <inttypes.h>
2323+#include <errno.h>
222423252426/*
···5355}
54565557static xrt_result_t
5858+swapchain_inc_image_use(struct xrt_swapchain *xsc, uint32_t index)
5959+{
6060+ struct comp_swapchain *sc = comp_swapchain(xsc);
6161+6262+ SWAPCHAIN_TRACE_BEGIN(swapchain_inc_image_use);
6363+6464+ VK_TRACE(sc->vk, "%p INC_IMAGE %d (use %d)", (void *)sc, index, sc->images[index].use_count);
6565+6666+ os_mutex_lock(&sc->images[index].use_mutex);
6767+ sc->images[index].use_count++;
6868+ os_mutex_unlock(&sc->images[index].use_mutex);
6969+7070+ SWAPCHAIN_TRACE_END(swapchain_inc_image_use);
7171+7272+ return XRT_SUCCESS;
7373+}
7474+7575+static xrt_result_t
7676+swapchain_dec_image_use(struct xrt_swapchain *xsc, uint32_t index)
7777+{
7878+ struct comp_swapchain *sc = comp_swapchain(xsc);
7979+8080+ SWAPCHAIN_TRACE_BEGIN(swapchain_dec_image_use);
8181+8282+ VK_TRACE(sc->vk, "%p DEC_IMAGE %d (use %d)", (void *)sc, index, sc->images[index].use_count);
8383+8484+ os_mutex_lock(&sc->images[index].use_mutex);
8585+8686+ assert(sc->images[index].use_count > 0 && "use count already 0");
8787+8888+ sc->images[index].use_count--;
8989+ if (sc->images[index].use_count == 0) {
9090+ os_mutex_unlock(&sc->images[index].use_mutex);
9191+ pthread_cond_broadcast(&sc->images[index].use_cond);
9292+ }
9393+9494+ os_mutex_unlock(&sc->images[index].use_mutex);
9595+9696+ SWAPCHAIN_TRACE_END(swapchain_dec_image_use);
9797+9898+ return XRT_SUCCESS;
9999+}
100100+101101+static xrt_result_t
56102swapchain_wait_image(struct xrt_swapchain *xsc, uint64_t timeout_ns, uint32_t index)
57103{
58104 struct comp_swapchain *sc = comp_swapchain(xsc);
591056060- VK_TRACE(sc->vk, "WAIT_IMAGE");
106106+ SWAPCHAIN_TRACE_BEGIN(swapchain_wait_image);
107107+108108+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d (use %d)", (void *)sc, index, sc->images[index].use_count);
109109+110110+ os_mutex_lock(&sc->images[index].use_mutex);
111111+112112+ if (sc->images[index].use_count == 0) {
113113+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d: NO WAIT", (void *)sc, index);
114114+ os_mutex_unlock(&sc->images[index].use_mutex);
115115+ SWAPCHAIN_TRACE_END(swapchain_wait_image);
116116+ return XRT_SUCCESS;
117117+ }
118118+119119+ // on windows pthread_cond_timedwait can not be used with monotonic time
120120+ uint64_t start_wait_rt = os_realtime_get_ns();
121121+122122+ uint64_t end_wait_rt;
123123+ // don't wrap on big or indefinite timeout
124124+ if (start_wait_rt > UINT64_MAX - timeout_ns) {
125125+ end_wait_rt = UINT64_MAX;
126126+ } else {
127127+ end_wait_rt = start_wait_rt + timeout_ns;
128128+ }
129129+130130+ struct timespec spec;
131131+ os_ns_to_timespec(end_wait_rt, &spec);
132132+133133+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d (use %d) start wait at: %" PRIu64 " (timeout at %" PRIu64 ")", (void *)sc,
134134+ index, sc->images[index].use_count, start_wait_rt, end_wait_rt);
135135+136136+ int ret;
137137+ while (sc->images[index].use_count > 0) {
138138+ // use pthread_cond_timedwait to implement timeout behavior
139139+ ret = pthread_cond_timedwait(&sc->images[index].use_cond, &sc->images[index].use_mutex.mutex, &spec);
140140+141141+ uint64_t now_rt = os_realtime_get_ns();
142142+ double diff = time_ns_to_ms_f(now_rt - start_wait_rt);
143143+144144+ if (ret == 0) {
145145+146146+ if (sc->images[index].use_count == 0) {
147147+ // image became available within timeout limits
148148+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d: success at %" PRIu64 " after %fms", (void *)sc,
149149+ index, now_rt, diff);
150150+ os_mutex_unlock(&sc->images[index].use_mutex);
151151+ SWAPCHAIN_TRACE_END(swapchain_wait_image);
152152+ return XRT_SUCCESS;
153153+ } else {
154154+ // cond got signaled but image is still in use, continue waiting
155155+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d: woken at %" PRIu64 " after %fms but still (%d use)",
156156+ (void *)sc, index, now_rt, diff, sc->images[index].use_count);
157157+ continue;
158158+ }
159159+160160+ } else if (ret == ETIMEDOUT) {
161161+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d (use %d): timeout at %" PRIu64 " after %fms", (void *)sc,
162162+ index, sc->images[index].use_count, now_rt, diff);
163163+164164+ if (now_rt >= end_wait_rt) {
165165+ // image did not become available within timeout limits
166166+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d (use %d): timeout (%" PRIu64 " > %" PRIu64 ")",
167167+ (void *)sc, index, sc->images[index].use_count, now_rt, end_wait_rt);
168168+ os_mutex_unlock(&sc->images[index].use_mutex);
169169+ SWAPCHAIN_TRACE_END(swapchain_wait_image);
170170+ return XRT_TIMEOUT;
171171+172172+ } else {
173173+ // spurious cond wakeup
174174+ VK_TRACE(sc->vk,
175175+ "%p WAIT_IMAGE %d (use %d): spurious timeout at %" PRIu64 " (%fms to timeout)",
176176+ (void *)sc, index, sc->images[index].use_count, now_rt,
177177+ time_ns_to_ms_f(end_wait_rt - now_rt));
178178+ continue;
179179+ }
180180+181181+ } else {
182182+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d: condition variable error %d", (void *)sc, index, ret);
183183+ os_mutex_unlock(&sc->images[index].use_mutex);
184184+ SWAPCHAIN_TRACE_END(swapchain_wait_image);
185185+ return XRT_ERROR_VULKAN;
186186+ }
187187+ }
188188+189189+ VK_TRACE(sc->vk, "%p WAIT_IMAGE %d: became available before spurious wakeup %d", (void *)sc, index, ret);
190190+191191+ os_mutex_unlock(&sc->images[index].use_mutex);
192192+ SWAPCHAIN_TRACE_END(swapchain_wait_image);
193193+61194 return XRT_SUCCESS;
62195}
63196···99232{
100233 sc->base.base.destroy = swapchain_destroy;
101234 sc->base.base.acquire_image = swapchain_acquire_image;
235235+ sc->base.base.inc_image_use = swapchain_inc_image_use;
236236+ sc->base.base.dec_image_use = swapchain_dec_image_use;
102237 sc->base.base.wait_image = swapchain_wait_image;
103238 sc->base.base.release_image = swapchain_release_image;
104239 sc->base.base.image_count = image_count;
···228363 //! @todo Propegate error
229364 VK_ERROR(vk, "Failed to barrier images");
230365 }
366366+367367+ for (uint32_t i = 0; i < image_count; i++) {
368368+369369+ ret = pthread_cond_init(&sc->images[i].use_cond, NULL);
370370+ if (ret) {
371371+ VK_ERROR(sc->vk, "Failed to init image use cond: %d", ret);
372372+ continue;
373373+ }
374374+375375+ ret = os_mutex_init(&sc->images[i].use_mutex);
376376+ if (ret) {
377377+ VK_ERROR(sc->vk, "Failed to init image use mutex: %d", ret);
378378+ continue;
379379+ }
380380+381381+ sc->images[i].use_count = 0;
382382+ }
231383}
232384233385static void
···379531 struct vk_bundle *vk = sc->vk;
380532381533 VK_TRACE(vk, "REALLY DESTROY");
534534+535535+ for (uint32_t i = 0; i < sc->base.base.image_count; i++) {
536536+ // compositor ensures to garbage collect after gpu work finished
537537+ if (sc->images[i].use_count != 0) {
538538+ VK_ERROR(vk, "swapchain destroy while image %d use count %d", i, sc->images[i].use_count);
539539+ assert(false);
540540+ continue; // leaking better than crashing?
541541+ }
542542+543543+ os_mutex_destroy(&sc->images[i].use_mutex);
544544+ pthread_cond_destroy(&sc->images[i].use_cond);
545545+ }
382546383547 for (uint32_t i = 0; i < sc->base.base.image_count; i++) {
384548 image_cleanup(vk, &sc->images[i]);
+9
src/xrt/compositor/util/comp_swapchain.h
···6464 } views;
6565 //! The number of array slices in a texture, 1 == regular 2D texture.
6666 size_t array_size;
6767+6868+ //! A usage counter, similar to a reference counter.
6969+ uint32_t use_count;
7070+7171+ //! A condition variable per swapchain image that is notified when @ref use_count count reaches 0.
7272+ pthread_cond_t use_cond;
7373+7474+ //! A mutex per swapchain image that is used with @ref use_cond.
7575+ struct os_mutex use_mutex;
6776};
68776978/*!