mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 02:51:10 +00:00
fix: retain Vulkan semaphore dependencies
This commit is contained in:
1 parent
9b91c02e07
commit
bb58d0cc7b
4 files changed
+218
-190
No files matched your search
@@ -292,8 +292,7 @@ A minimal native Vulkan probe on the real Quest reports export/import support
|
||||
for SYNC_FD fences/semaphores, but not OPAQUE_FD handles. This distinguishes the
|
||||
unsupported implicit DMA-buf fence export from available explicit Vulkan GPU
|
||||
completion primitives. Implementing the explicit producer synchronization is
|
||||
still necessary; the existing shared-semaphore wait-dropping workaround must
|
||||
not be treated as correct synchronization. The desktop's dashboard helper also
|
||||
still necessary. The desktop's dashboard helper also
|
||||
lacks X display authorization; no authentication or permission check was
|
||||
bypassed to work around that error.
|
||||
|
||||
@@ -310,6 +309,18 @@ bundled into the user-facing runtime. A valid already-signaled SYNC_FD value of
|
||||
|
||||
## Remaining stages
|
||||
|
||||
The Vulkan layer no longer drops imported binary waits or unsignaled timeline
|
||||
waits after a three-millisecond grace period. Queue submissions and host waits
|
||||
retain their original arguments, deadlines and downstream results. The
|
||||
software fallback supports permanent OPAQUE_FD timeline payloads only; binary,
|
||||
temporary and malformed fallback imports fail explicitly without consuming
|
||||
the caller's descriptor. Native handles still pass through unchanged. Host
|
||||
regressions exercise both submit APIs, pending values, device-loss propagation,
|
||||
65-semaphore host waits, import failures, re-export and descriptor cleanup.
|
||||
These are API-contract tests, not Quest GPU or SteamVR game validation. Native
|
||||
binary SYNC_FD support remains available, but adapting SteamVR's unsupported
|
||||
OPAQUE_FD binary usage still requires a correctly synchronized implementation.
|
||||
|
||||
Steam's ARM64 watchdog also aborts with `futex robust_list not initialized by
|
||||
pthreads`. A captured preload trace showed successful own-thread queries
|
||||
leaving `errno=EFAULT`. The preload now prefers the real kernel query, falls
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("gcc"), "needs gcc on a Linux host")
|
||||
class VrSemaphoreLayerTest(unittest.TestCase):
|
||||
def test_waits_are_never_fabricated(self):
|
||||
repository = Path(__file__).resolve().parents[2]
|
||||
candidates = [os.environ.get("VK_INCLUDE", ""), "/usr/include", repository / "build/vr/include"]
|
||||
candidates += [path for root in (os.environ.get("ANDROID_HOME"), os.environ.get("ANDROID_SDK_ROOT")) if root
|
||||
for path in Path(root).glob("ndk/*/toolchains/llvm/prebuilt/*/sysroot/usr/include")]
|
||||
headers = next((Path(path) for path in candidates if path and (Path(path) / "vulkan/vk_layer.h").is_file()), None)
|
||||
if headers is None:
|
||||
self.skipTest("needs Vulkan headers")
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
executable = Path(directory) / "semaphore-layer-test"
|
||||
subprocess.run(["gcc", "-std=c11", "-O2", "-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter",
|
||||
"-pthread", "-I" + str(headers), str(repository / "tools/vr/tests/test_semaphore_layer.c"),
|
||||
"-ldl", "-o", str(executable)], check=True)
|
||||
result = subprocess.run([str(executable)], capture_output=True, text=True, timeout=20)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
self.assertIn("fd ownership passed", result.stdout)
|
||||
@@ -30,13 +30,20 @@ static void say(const char *fmt, const char *arg) {
|
||||
|
||||
static void crash_handler(int sig, siginfo_t *si, void *uc_) {
|
||||
ucontext_t *uc = uc_;
|
||||
#if defined(__aarch64__)
|
||||
void *program_counter = (void *)uc->uc_mcontext.pc;
|
||||
#elif defined(__x86_64__)
|
||||
void *program_counter = (void *)uc->uc_mcontext.gregs[REG_RIP];
|
||||
#else
|
||||
void *program_counter = NULL;
|
||||
#endif
|
||||
char buf[512];
|
||||
int n = snprintf(buf, sizeof(buf), "\n[crashtrace] %s: signal %d addr %p pc %p\n", program_invocation_short_name, sig,
|
||||
si->si_addr, (void *)uc->uc_mcontext.pc);
|
||||
si->si_addr, program_counter);
|
||||
if (write(2, buf, n)) {}
|
||||
void *frames[48];
|
||||
int count = backtrace(frames, 48);
|
||||
if (count > 1) frames[1] = (void *)uc->uc_mcontext.pc;
|
||||
if (count > 1) frames[1] = program_counter;
|
||||
for (int i = 1; i < count; i++) {
|
||||
Dl_info info;
|
||||
if (dladdr(frames[i], &info) && info.dli_fname)
|
||||
@@ -477,7 +484,6 @@ struct bridged {
|
||||
int fd;
|
||||
int timeline;
|
||||
unsigned long ino;
|
||||
uint64_t submitted;
|
||||
};
|
||||
static pthread_mutex_t g_br_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct bridged g_br[MAX_SEMS];
|
||||
@@ -597,7 +603,10 @@ static VkResult VKAPI_CALL CreateSemaphore(VkDevice d, const VkSemaphoreCreateIn
|
||||
if (timeline) timeline_note(*out, 1);
|
||||
return r;
|
||||
}
|
||||
if (!export_info) return r;
|
||||
if (!export_info || !timeline ||
|
||||
((const VkExportSemaphoreCreateInfo *)export_info)->handleTypes != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) return r;
|
||||
for (const VkBaseInStructure *item = ci->pNext; item; item = item->pNext)
|
||||
if (item != export_info && item != (const void *)type_info) return r;
|
||||
VkSemaphoreTypeCreateInfo type = {0};
|
||||
VkSemaphoreCreateInfo mine = *ci;
|
||||
mine.pNext = NULL;
|
||||
@@ -612,13 +621,26 @@ static VkResult VKAPI_CALL CreateSemaphore(VkDevice d, const VkSemaphoreCreateIn
|
||||
int fd = memfd_create(SHARED_NAME, MFD_CLOEXEC);
|
||||
_Atomic uint64_t *counter = NULL;
|
||||
if (fd >= 0 && ftruncate(fd, 4096) == 0) counter = map_counter(fd);
|
||||
if (!counter) { if (fd >= 0) close(fd); return r; }
|
||||
if (!counter) {
|
||||
if (fd >= 0) close(fd);
|
||||
NEXT(d, DestroySemaphore)(d, *out, a);
|
||||
timeline_note(*out, 0);
|
||||
*out = VK_NULL_HANDLE;
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
}
|
||||
atomic_store(counter, timeline ? type_info->initialValue : 0);
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *slot = bridged_find(VK_NULL_HANDLE);
|
||||
if (slot) *slot = (struct bridged){*out, d, counter, fd, timeline, fd_ino(fd), 0};
|
||||
if (slot) *slot = (struct bridged){*out, d, counter, fd, timeline, fd_ino(fd)};
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
if (!slot) { munmap((void *)counter, 4096); close(fd); return r; }
|
||||
if (!slot) {
|
||||
munmap((void *)counter, 4096);
|
||||
close(fd);
|
||||
NEXT(d, DestroySemaphore)(d, *out, a);
|
||||
timeline_note(*out, 0);
|
||||
*out = VK_NULL_HANDLE;
|
||||
return VK_ERROR_TOO_MANY_OBJECTS;
|
||||
}
|
||||
if (timeline) poller_start();
|
||||
return r;
|
||||
}
|
||||
@@ -646,6 +668,11 @@ static VkResult VKAPI_CALL GetSemaphoreFdKHR(VkDevice d, const VkSemaphoreGetFdI
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *b = bridged_find(info->semaphore);
|
||||
int mine = b && b->fd >= 0 ? dup(b->fd) : -2;
|
||||
if (b && info->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
if (mine >= 0) close(mine);
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
return VK_ERROR_INVALID_EXTERNAL_HANDLE;
|
||||
}
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
if (mine == -2) return NEXT(d, GetSemaphoreFdKHR)(d, info, fd);
|
||||
*fd = mine;
|
||||
@@ -654,113 +681,29 @@ static VkResult VKAPI_CALL GetSemaphoreFdKHR(VkDevice d, const VkSemaphoreGetFdI
|
||||
|
||||
static VkResult VKAPI_CALL ImportSemaphoreFdKHR(VkDevice d, const VkImportSemaphoreFdInfoKHR *info) {
|
||||
if (info->fd < 0 || !is_shared_counter(info->fd)) return NEXT(d, ImportSemaphoreFdKHR)(d, info);
|
||||
_Atomic uint64_t *counter = map_counter(info->fd);
|
||||
const unsigned long ino = fd_ino(info->fd);
|
||||
close(info->fd);
|
||||
const int timeline = timeline_known(info->semaphore);
|
||||
struct stat metadata;
|
||||
if (!timeline || info->flags || info->handleType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT ||
|
||||
fstat(info->fd, &metadata) != 0 || metadata.st_size != 4096) return VK_ERROR_INVALID_EXTERNAL_HANDLE;
|
||||
_Atomic uint64_t *counter = map_counter(info->fd);
|
||||
if (!counter) return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
const unsigned long ino = fd_ino(info->fd);
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *b = bridged_find(info->semaphore);
|
||||
if (b) bridged_drop(b);
|
||||
b = bridged_find(VK_NULL_HANDLE);
|
||||
if (b && counter) *b = (struct bridged){info->semaphore, d, counter, -1, timeline, ino, 0};
|
||||
else if (counter) munmap((void *)counter, 4096);
|
||||
if (b) *b = (struct bridged){info->semaphore, d, counter, info->fd, timeline, ino};
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
if (!b) {
|
||||
munmap((void *)counter, 4096);
|
||||
return VK_ERROR_TOO_MANY_OBJECTS;
|
||||
}
|
||||
if (timeline) poller_start();
|
||||
static int said;
|
||||
if (said++ < 4) say(timeline ? "imported a shared timeline semaphore (%s): bridged" : "imported a shared binary semaphore (%s): its waits are dropped",
|
||||
program_invocation_short_name);
|
||||
if (said++ < 4) say("imported a shared timeline semaphore (%s): waits retained", program_invocation_short_name);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static int drop_wait(VkSemaphore s) {
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *b = bridged_find(s);
|
||||
int r = b && !b->timeline && b->fd < 0;
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static uint64_t now_us(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000000u + (uint64_t)ts.tv_nsec / 1000u;
|
||||
}
|
||||
|
||||
#define SUBMIT_GRACE_US 3000
|
||||
|
||||
static int submit_drop_wait(VkSemaphore s, uint64_t value) {
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *b = bridged_find(s);
|
||||
if (!b) { pthread_mutex_unlock(&g_br_lock); return 0; }
|
||||
if (!b->timeline) { int r = b->fd < 0; pthread_mutex_unlock(&g_br_lock); return r; }
|
||||
const int imported = b->fd < 0;
|
||||
const uint64_t queued = b->submitted;
|
||||
VkDevice dev = b->dev;
|
||||
_Atomic uint64_t *counter = b->counter;
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
if (!imported || !value || value <= queued) return 0;
|
||||
struct dev *d = dev_for(dev);
|
||||
if (!d) return 0;
|
||||
PFN_vkGetSemaphoreCounterValue get = (PFN_vkGetSemaphoreCounterValue)d->gdpa(dev, "vkGetSemaphoreCounterValue");
|
||||
if (!get) get = (PFN_vkGetSemaphoreCounterValue)d->gdpa(dev, "vkGetSemaphoreCounterValueKHR");
|
||||
PFN_vkSignalSemaphore sig = (PFN_vkSignalSemaphore)d->gdpa(dev, "vkSignalSemaphore");
|
||||
if (!sig) sig = (PFN_vkSignalSemaphore)d->gdpa(dev, "vkSignalSemaphoreKHR");
|
||||
if (!get) return 0;
|
||||
const uint64_t until = now_us() + SUBMIT_GRACE_US;
|
||||
for (;;) {
|
||||
uint64_t local = 0;
|
||||
if (get(dev, s, &local) == VK_SUCCESS && local >= value) return 0;
|
||||
uint64_t shared = atomic_load(counter);
|
||||
if (shared >= value && sig) {
|
||||
VkSemaphoreSignalInfo si = {VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, NULL, s, shared};
|
||||
if (sig(dev, &si) == VK_SUCCESS) return 0;
|
||||
}
|
||||
if (now_us() >= until) break;
|
||||
usleep(100);
|
||||
}
|
||||
static _Atomic int dropped;
|
||||
if (atomic_fetch_add(&dropped, 1) == 0)
|
||||
say("%s: dropping a submit's wait on a shared value nobody has signalled yet (Turnip would block the submit)",
|
||||
program_invocation_short_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void note_signal(VkSemaphore s, uint64_t value) {
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
struct bridged *b = bridged_find(s);
|
||||
if (b && b->timeline && value > b->submitted) b->submitted = value;
|
||||
pthread_mutex_unlock(&g_br_lock);
|
||||
}
|
||||
|
||||
static void note_submit_signals(uint32_t count, const VkSubmitInfo *submits) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
const VkTimelineSemaphoreSubmitInfo *t = NULL;
|
||||
for (const VkBaseInStructure *p = submits[i].pNext; p; p = p->pNext)
|
||||
if (p->sType == VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO) t = (const void *)p;
|
||||
for (uint32_t j = 0; t && j < submits[i].signalSemaphoreCount && j < t->signalSemaphoreValueCount; j++)
|
||||
note_signal(submits[i].pSignalSemaphores[j], t->pSignalSemaphoreValues[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static void note_submit2_signals(uint32_t count, const VkSubmitInfo2 *submits) {
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
for (uint32_t j = 0; j < submits[i].signalSemaphoreInfoCount; j++)
|
||||
note_signal(submits[i].pSignalSemaphoreInfos[j].semaphore, submits[i].pSignalSemaphoreInfos[j].value);
|
||||
}
|
||||
|
||||
static uint32_t keep_waits(const VkSemaphore *in, const VkPipelineStageFlags *stages, const uint64_t *values, uint32_t n,
|
||||
VkSemaphore *out, VkPipelineStageFlags *out_stages, uint64_t *out_values) {
|
||||
uint32_t k = 0;
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
if (submit_drop_wait(in[i], values ? values[i] : 0)) continue;
|
||||
out[k] = in[i];
|
||||
if (stages) out_stages[k] = stages[i];
|
||||
if (values) out_values[k] = values[i];
|
||||
k++;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
static void trace_sem(const char *what, VkSemaphore sem, uint64_t value) {
|
||||
if (!sem_trace_on() || value > 3) return;
|
||||
pthread_mutex_lock(&g_br_lock);
|
||||
@@ -786,55 +729,7 @@ static VkResult VKAPI_CALL QueueSubmit(VkQueue q, uint32_t count, const VkSubmit
|
||||
for (uint32_t j = 0; j < submits[i].signalSemaphoreCount; j++)
|
||||
trace_sem("signals", submits[i].pSignalSemaphores[j], t && j < t->signalSemaphoreValueCount ? t->pSignalSemaphoreValues[j] : 0);
|
||||
}
|
||||
int any = 0;
|
||||
for (uint32_t i = 0; i < count && !any; i++) {
|
||||
const VkTimelineSemaphoreSubmitInfo *t = NULL;
|
||||
for (const VkBaseInStructure *p = submits[i].pNext; p; p = p->pNext)
|
||||
if (p->sType == VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO) t = (const void *)p;
|
||||
for (uint32_t j = 0; j < submits[i].waitSemaphoreCount && !any; j++)
|
||||
any = submit_drop_wait(submits[i].pWaitSemaphores[j], t && j < t->waitSemaphoreValueCount ? t->pWaitSemaphoreValues[j] : 0);
|
||||
}
|
||||
if (!any) {
|
||||
VkResult r = next(q, count, submits, fence);
|
||||
note_submit_signals(count, submits);
|
||||
return r;
|
||||
}
|
||||
VkSubmitInfo *copy = calloc(count, sizeof(*copy));
|
||||
VkTimelineSemaphoreSubmitInfo *tl = calloc(count, sizeof(*tl));
|
||||
void **bufs = calloc(count * 3, sizeof(void *));
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
copy[i] = submits[i];
|
||||
uint32_t n = submits[i].waitSemaphoreCount;
|
||||
if (!n) continue;
|
||||
VkSemaphore *sems = calloc(n, sizeof(*sems));
|
||||
VkPipelineStageFlags *stages = calloc(n, sizeof(*stages));
|
||||
uint64_t *vals = calloc(n, sizeof(*vals));
|
||||
bufs[i * 3] = sems; bufs[i * 3 + 1] = stages; bufs[i * 3 + 2] = vals;
|
||||
const VkTimelineSemaphoreSubmitInfo *t = NULL;
|
||||
for (const VkBaseInStructure *p = submits[i].pNext; p; p = p->pNext)
|
||||
if (p->sType == VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO) t = (const void *)p;
|
||||
uint32_t k = keep_waits(submits[i].pWaitSemaphores, submits[i].pWaitDstStageMask,
|
||||
t && t->waitSemaphoreValueCount ? t->pWaitSemaphoreValues : NULL, n, sems, stages, vals);
|
||||
copy[i].waitSemaphoreCount = k;
|
||||
copy[i].pWaitSemaphores = sems;
|
||||
copy[i].pWaitDstStageMask = stages;
|
||||
if (t) {
|
||||
tl[i] = *t;
|
||||
tl[i].waitSemaphoreValueCount = t->waitSemaphoreValueCount ? k : 0;
|
||||
tl[i].pWaitSemaphoreValues = vals;
|
||||
const void *rest = NULL;
|
||||
const VkBaseInStructure *prev = NULL;
|
||||
for (const VkBaseInStructure *p = submits[i].pNext; p; prev = p, p = p->pNext) if (p == (const void *)t) { rest = p->pNext; break; }
|
||||
(void)prev;
|
||||
tl[i].pNext = rest;
|
||||
copy[i].pNext = &tl[i];
|
||||
}
|
||||
}
|
||||
VkResult r = next(q, count, copy, fence);
|
||||
note_submit_signals(count, submits);
|
||||
for (uint32_t i = 0; i < count * 3; i++) free(bufs[i]);
|
||||
free(bufs); free(tl); free(copy);
|
||||
return r;
|
||||
return next(q, count, submits, fence);
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL QueueSubmit2(VkQueue q, uint32_t count, const VkSubmitInfo2 *submits, VkFence fence) {
|
||||
@@ -847,47 +742,13 @@ static VkResult VKAPI_CALL QueueSubmit2(VkQueue q, uint32_t count, const VkSubmi
|
||||
for (uint32_t j = 0; j < submits[i].signalSemaphoreInfoCount; j++)
|
||||
trace_sem("signals", submits[i].pSignalSemaphoreInfos[j].semaphore, submits[i].pSignalSemaphoreInfos[j].value);
|
||||
}
|
||||
int any = 0;
|
||||
for (uint32_t i = 0; i < count && !any; i++)
|
||||
for (uint32_t j = 0; j < submits[i].waitSemaphoreInfoCount && !any; j++)
|
||||
any = submit_drop_wait(submits[i].pWaitSemaphoreInfos[j].semaphore, submits[i].pWaitSemaphoreInfos[j].value);
|
||||
if (!any) {
|
||||
VkResult r = next(q, count, submits, fence);
|
||||
note_submit2_signals(count, submits);
|
||||
return r;
|
||||
}
|
||||
VkSubmitInfo2 *copy = calloc(count, sizeof(*copy));
|
||||
VkSemaphoreSubmitInfo **waits = calloc(count, sizeof(*waits));
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
copy[i] = submits[i];
|
||||
uint32_t n = submits[i].waitSemaphoreInfoCount, k = 0;
|
||||
waits[i] = calloc(n + 1, sizeof(VkSemaphoreSubmitInfo));
|
||||
for (uint32_t j = 0; j < n; j++)
|
||||
if (!submit_drop_wait(submits[i].pWaitSemaphoreInfos[j].semaphore, submits[i].pWaitSemaphoreInfos[j].value))
|
||||
waits[i][k++] = submits[i].pWaitSemaphoreInfos[j];
|
||||
copy[i].waitSemaphoreInfoCount = k;
|
||||
copy[i].pWaitSemaphoreInfos = waits[i];
|
||||
}
|
||||
VkResult r = next(q, count, copy, fence);
|
||||
note_submit2_signals(count, submits);
|
||||
for (uint32_t i = 0; i < count; i++) free(waits[i]);
|
||||
free(waits); free(copy);
|
||||
return r;
|
||||
return next(q, count, submits, fence);
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL WaitSemaphores(VkDevice d, const VkSemaphoreWaitInfo *info, uint64_t timeout) {
|
||||
PFN_vkWaitSemaphores next = (PFN_vkWaitSemaphores)dev_for(d)->gdpa(d, "vkWaitSemaphores");
|
||||
if (!next) next = (PFN_vkWaitSemaphores)dev_for(d)->gdpa(d, "vkWaitSemaphoresKHR");
|
||||
VkSemaphore sems[64]; uint64_t vals[64]; uint32_t k = 0;
|
||||
for (uint32_t i = 0; i < info->semaphoreCount && k < 64; i++)
|
||||
if (!drop_wait(info->pSemaphores[i])) { sems[k] = info->pSemaphores[i]; vals[k] = info->pValues[i]; k++; }
|
||||
if (k == info->semaphoreCount) {
|
||||
return next(d, info, timeout);
|
||||
}
|
||||
if (!k) return VK_SUCCESS;
|
||||
VkSemaphoreWaitInfo mine = *info;
|
||||
mine.semaphoreCount = k; mine.pSemaphores = sems; mine.pValues = vals;
|
||||
return next(d, &mine, timeout);
|
||||
return next(d, info, timeout);
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL GetSemaphoreCounterValue(VkDevice d, VkSemaphore s, uint64_t *value) {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include "../src/vk_layer_droiddeck_vr.c"
|
||||
|
||||
static const VkSubmitInfo *expected_submit;
|
||||
static const VkSubmitInfo2 *expected_submit2;
|
||||
static const VkSemaphoreWaitInfo *expected_wait;
|
||||
static unsigned create_calls;
|
||||
static unsigned destroyed;
|
||||
static VkResult submit_result = VK_SUCCESS;
|
||||
|
||||
static VkResult VKAPI_CALL mock_submit(VkQueue queue, uint32_t count, const VkSubmitInfo *submits, VkFence fence) {
|
||||
assert(queue && count == 1 && fence == VK_NULL_HANDLE);
|
||||
assert(submits == expected_submit);
|
||||
assert(submits->waitSemaphoreCount == 1);
|
||||
return submit_result;
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL mock_submit2(VkQueue queue, uint32_t count, const VkSubmitInfo2 *submits, VkFence fence) {
|
||||
assert(queue && count == 1 && fence == VK_NULL_HANDLE);
|
||||
assert(submits == expected_submit2);
|
||||
assert(submits->waitSemaphoreInfoCount == 1);
|
||||
return submit_result;
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL mock_wait(VkDevice device, const VkSemaphoreWaitInfo *info, uint64_t timeout) {
|
||||
assert(device && info == expected_wait && timeout == 7654321);
|
||||
assert(info->semaphoreCount == 65);
|
||||
return VK_TIMEOUT;
|
||||
}
|
||||
|
||||
static VkResult VKAPI_CALL mock_create(VkDevice device, const VkSemaphoreCreateInfo *info,
|
||||
const VkAllocationCallbacks *allocator, VkSemaphore *output) {
|
||||
(void)device;
|
||||
(void)allocator;
|
||||
create_calls++;
|
||||
for (const VkBaseInStructure *item = info->pNext; item; item = item->pNext)
|
||||
if (item->sType == VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO) return VK_ERROR_INVALID_EXTERNAL_HANDLE;
|
||||
*output = (VkSemaphore)(uintptr_t)(create_calls + 100);
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void VKAPI_CALL mock_destroy(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *allocator) {
|
||||
(void)device;
|
||||
(void)semaphore;
|
||||
(void)allocator;
|
||||
destroyed++;
|
||||
}
|
||||
|
||||
static PFN_vkVoidFunction VKAPI_CALL mock_proc(VkDevice device, const char *name) {
|
||||
(void)device;
|
||||
if (!strcmp(name, "vkQueueSubmit")) return (PFN_vkVoidFunction)mock_submit;
|
||||
if (!strcmp(name, "vkQueueSubmit2")) return (PFN_vkVoidFunction)mock_submit2;
|
||||
if (!strcmp(name, "vkWaitSemaphores")) return (PFN_vkVoidFunction)mock_wait;
|
||||
if (!strcmp(name, "vkCreateSemaphore")) return (PFN_vkVoidFunction)mock_create;
|
||||
if (!strcmp(name, "vkDestroySemaphore")) return (PFN_vkVoidFunction)mock_destroy;
|
||||
assert(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
setenv("DROIDDECK_VR_LAYER_QUIET", "1", 1);
|
||||
void *dispatch = &dispatch;
|
||||
VkDevice device = (VkDevice)&dispatch;
|
||||
VkQueue queue = (VkQueue)&dispatch;
|
||||
g_dev[0] = (struct dev){device, mock_proc, NULL, 1, 0};
|
||||
g_poller_started = 1;
|
||||
VkExportSemaphoreCreateInfo export_info = {VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, NULL,
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
|
||||
VkSemaphoreCreateInfo create = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &export_info, 0};
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
assert(CreateSemaphore(device, &create, NULL, &semaphore) == VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
assert(create_calls == 1 && !semaphore);
|
||||
VkSemaphoreTypeCreateInfo type = {VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, &export_info,
|
||||
VK_SEMAPHORE_TYPE_TIMELINE, 0};
|
||||
create.pNext = &type;
|
||||
assert(CreateSemaphore(device, &create, NULL, &semaphore) == VK_SUCCESS);
|
||||
struct bridged *bridge = bridged_find(semaphore);
|
||||
assert(bridge && atomic_load(bridge->counter) == 0);
|
||||
VkPipelineStageFlags stage = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
uint64_t value = 9;
|
||||
VkTimelineSemaphoreSubmitInfo timeline = {VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, NULL, 1, &value, 0, NULL};
|
||||
VkSubmitInfo submit = {VK_STRUCTURE_TYPE_SUBMIT_INFO, &timeline, 1, &semaphore, &stage, 0, NULL, 0, NULL};
|
||||
expected_submit = &submit;
|
||||
assert(QueueSubmit(queue, 1, &submit, VK_NULL_HANDLE) == VK_SUCCESS);
|
||||
assert(atomic_load(bridge->counter) == 0);
|
||||
VkSemaphoreSubmitInfo wait2 = {VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, NULL, semaphore, value,
|
||||
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0};
|
||||
VkSubmitInfo2 submit2 = {VK_STRUCTURE_TYPE_SUBMIT_INFO_2, NULL, 0, 1, &wait2, 0, NULL, 0, NULL};
|
||||
expected_submit2 = &submit2;
|
||||
assert(QueueSubmit2(queue, 1, &submit2, VK_NULL_HANDLE) == VK_SUCCESS);
|
||||
submit_result = VK_ERROR_DEVICE_LOST;
|
||||
assert(QueueSubmit(queue, 1, &submit, VK_NULL_HANDLE) == VK_ERROR_DEVICE_LOST);
|
||||
assert(QueueSubmit2(queue, 1, &submit2, VK_NULL_HANDLE) == VK_ERROR_DEVICE_LOST);
|
||||
VkSemaphore semaphores[65];
|
||||
uint64_t values[65];
|
||||
for (unsigned index = 0; index < 65; index++) { semaphores[index] = semaphore; values[index] = value; }
|
||||
VkSemaphoreWaitInfo wait = {VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, NULL, VK_SEMAPHORE_WAIT_ANY_BIT, 65, semaphores, values};
|
||||
expected_wait = &wait;
|
||||
assert(WaitSemaphores(device, &wait, 7654321) == VK_TIMEOUT);
|
||||
int descriptor = dup(bridge->fd);
|
||||
VkSemaphore binary = (VkSemaphore)(uintptr_t)999;
|
||||
VkImportSemaphoreFdInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, NULL, binary, 0,
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, descriptor};
|
||||
assert(ImportSemaphoreFdKHR(device, &import_info) == VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
assert(fcntl(descriptor, F_GETFD) >= 0);
|
||||
timeline_note(binary, 1);
|
||||
assert(ftruncate(descriptor, 32) == 0);
|
||||
assert(ImportSemaphoreFdKHR(device, &import_info) == VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
assert(ftruncate(descriptor, 4096) == 0);
|
||||
import_info.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
|
||||
assert(ImportSemaphoreFdKHR(device, &import_info) == VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
import_info.flags = 0;
|
||||
assert(ImportSemaphoreFdKHR(device, &import_info) == VK_SUCCESS);
|
||||
VkSemaphoreGetFdInfoKHR get_info = {VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, NULL, binary,
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT};
|
||||
int reexported = -1;
|
||||
assert(GetSemaphoreFdKHR(device, &get_info, &reexported) == VK_SUCCESS && reexported >= 0);
|
||||
assert(fd_ino(reexported) == fd_ino(descriptor));
|
||||
close(reexported);
|
||||
get_info.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
|
||||
assert(GetSemaphoreFdKHR(device, &get_info, &reexported) == VK_ERROR_INVALID_EXTERNAL_HANDLE);
|
||||
DestroySemaphore(device, binary, NULL);
|
||||
assert(fcntl(descriptor, F_GETFD) == -1 && errno == EBADF);
|
||||
DestroySemaphore(device, semaphore, NULL);
|
||||
assert(destroyed == 2 && !bridged_find(semaphore));
|
||||
puts("semaphore layer: pending waits retained, errors propagated, unsupported binary imports refused, fd ownership passed");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user