From e40cd36151c80fe93073361959e30d5b20bf59d2 Mon Sep 17 00:00:00 2001 From: The412Banner Date: Tue, 13 Oct 2026 22:39:06 -0400 Subject: [PATCH] Non-Adreno round 7: bridge command-buffer guard, Venus Zink trace, PowerVR mangoapp, Xclipse label - vkbridge server: every command buffer a device hands out is tracked; Begin, End (stream replay) and QueueSubmit refuse one it never allocated or has freed (VK_ERROR_DEVICE_LOST, logged) instead of passing it to the driver, where Mali's vkBeginCommandBuffer crashed the app (SIGSEGV 0x10, Steam's Zink on an Edge 60 and a Tab S11). - vkbridge server: a fault inside the driver while serving a request cuts that client off (SIGSEGV/SIGBUS guard per request) and leaves its objects alone; the app and the other clients carry on. Other faults go to the previous handler. - vkbridge server: the pipeline cache directory is copied; the JNI string it pointed at was freed (garbage in the cache path). - Venus: DROIDDECK_MALI_COMPAT_TRACE=1 makes the Mali compatibility layer log what an application queries before vkCreateDevice; a failed glprobe adds a traced run and vulkaninfo --summary under the same environment. - PowerVR: mangoapp draws with llvmpipe (Zink could not make it a swapchain and it crashed at every start). - Setup: an Xclipse GPU reads "Experimental: RADV or the system Xclipse driver", not the PowerVR line. --- app/src/main/cpp/vkbridge/server_cbtrack.c | 194 ++++++++++++++++++ app/src/main/cpp/vkbridge/server_conn.c | 75 ++++++- app/src/main/cpp/vkbridge/server_instance.c | 2 + app/src/main/cpp/vkbridge/server_pcache.c | 6 +- app/src/main/cpp/vkbridge/vkb_server.h | 7 + .../com/droiddeck/launcher/gpu/GpuInfo.kt | 2 +- app/src/main/res/values/strings.xml | 1 + .../overlay/usr/local/bin/droiddeck-session | 4 + tools/mangoapp/mangoapp | 7 + tools/venus/layer/layer.c | 29 +++ 10 files changed, 324 insertions(+), 3 deletions(-) create mode 100644 app/src/main/cpp/vkbridge/server_cbtrack.c diff --git a/app/src/main/cpp/vkbridge/server_cbtrack.c b/app/src/main/cpp/vkbridge/server_cbtrack.c new file mode 100644 index 00000000..757401ef --- /dev/null +++ b/app/src/main/cpp/vkbridge/server_cbtrack.c @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * + * The command buffers each device has handed out, so that a handle the driver never gave (or has + * since freed, with its pool) is refused here instead of reaching the driver. Handles arrive from + * clients as plain numbers; one the driver did not know took the whole app down inside Mali's + * vkBeginCommandBuffer (SIGSEGV at 0x10, Steam's Zink on an Edge 60 and a Tab S11). A refused + * command buffer fails its call with VK_ERROR_DEVICE_LOST, which the client reports, and is logged + * once per handle. + */ +#define _GNU_SOURCE +#include "vkb_server.h" + +#include +#include + +#define CBT_BUCKETS 1024 + +typedef struct cbt_entry { + VkCommandBuffer cb; + VkCommandPool pool; + struct cbt_entry *next; +} cbt_entry; + +typedef struct vkb_cbtrack { + pthread_mutex_t lock; + cbt_entry *b[CBT_BUCKETS]; + VkCommandBuffer last_refused; + PFN_vkAllocateCommandBuffers alloc; + PFN_vkFreeCommandBuffers free_cbs; + PFN_vkDestroyCommandPool destroy_pool; + PFN_vkBeginCommandBuffer begin; + PFN_vkQueueSubmit submit; + PFN_vkQueueSubmit2 submit2; +} vkb_cbtrack; + +static unsigned bucket(VkCommandBuffer cb) +{ + uint64_t k = (uint64_t)(uintptr_t)cb; + k ^= k >> 31; + k *= 0x9e3779b97f4a7c15ull; + return (unsigned)(k >> 54) & (CBT_BUCKETS - 1); +} + +static vkb_cbtrack *cur(void) +{ + vkb_srv_table *t = vkb_srv_current_table(); + return t ? t->cbtrack : NULL; +} + +static int known_locked(vkb_cbtrack *k, VkCommandBuffer cb) +{ + for (cbt_entry *e = k->b[bucket(cb)]; e; e = e->next) + if (e->cb == cb) return 1; + return 0; +} + +int vkb_cbtrack_known(vkb_srv_table *t, VkCommandBuffer cb, const char *what) +{ + vkb_cbtrack *k = t ? t->cbtrack : NULL; + if (!k) return cb != VK_NULL_HANDLE; + pthread_mutex_lock(&k->lock); + int ok = cb && known_locked(k, cb); + int first = !ok && k->last_refused != cb; + if (!ok) k->last_refused = cb; + pthread_mutex_unlock(&k->lock); + if (first) + VKB_ERR("device %u: %s on command buffer %p, which this device never allocated or has freed; refused (the client " + "gets VK_ERROR_DEVICE_LOST)", t->id, what, (void *)cb); + return ok; +} + +static VKAPI_ATTR VkResult VKAPI_CALL cbt_AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *info, + VkCommandBuffer *out) +{ + vkb_cbtrack *k = cur(); + VkResult r = k->alloc(device, info, out); + if (r != VK_SUCCESS || !info) return r; + pthread_mutex_lock(&k->lock); + for (uint32_t i = 0; i < info->commandBufferCount; i++) { + if (!out[i] || known_locked(k, out[i])) continue; + cbt_entry *e = malloc(sizeof(*e)); + if (!e) continue; + e->cb = out[i]; + e->pool = info->commandPool; + unsigned h = bucket(out[i]); + e->next = k->b[h]; + k->b[h] = e; + } + pthread_mutex_unlock(&k->lock); + return r; +} + +static void forget_locked(vkb_cbtrack *k, VkCommandBuffer cb, VkCommandPool pool) +{ + for (unsigned h = cb ? bucket(cb) : 0; h < CBT_BUCKETS; h = cb ? CBT_BUCKETS : h + 1) { + cbt_entry **pp = &k->b[h]; + while (*pp) { + cbt_entry *e = *pp; + if ((cb && e->cb == cb) || (!cb && e->pool == pool)) { + *pp = e->next; + free(e); + } else { + pp = &e->next; + } + } + } +} + +static VKAPI_ATTR void VKAPI_CALL cbt_FreeCommandBuffers(VkDevice device, VkCommandPool pool, uint32_t n, const VkCommandBuffer *cbs) +{ + vkb_cbtrack *k = cur(); + pthread_mutex_lock(&k->lock); + for (uint32_t i = 0; i < n; i++) + if (cbs[i]) forget_locked(k, cbs[i], pool); + pthread_mutex_unlock(&k->lock); + k->free_cbs(device, pool, n, cbs); +} + +static VKAPI_ATTR void VKAPI_CALL cbt_DestroyCommandPool(VkDevice device, VkCommandPool pool, const VkAllocationCallbacks *a) +{ + vkb_cbtrack *k = cur(); + if (pool) { + pthread_mutex_lock(&k->lock); + forget_locked(k, VK_NULL_HANDLE, pool); + pthread_mutex_unlock(&k->lock); + } + k->destroy_pool(device, pool, a); +} + +static VKAPI_ATTR VkResult VKAPI_CALL cbt_BeginCommandBuffer(VkCommandBuffer cb, const VkCommandBufferBeginInfo *info) +{ + vkb_cbtrack *k = cur(); + if (!vkb_cbtrack_known(vkb_srv_current_table(), cb, "vkBeginCommandBuffer")) return VK_ERROR_DEVICE_LOST; + return k->begin(cb, info); +} + +static VKAPI_ATTR VkResult VKAPI_CALL cbt_QueueSubmit(VkQueue queue, uint32_t n, const VkSubmitInfo *s, VkFence fence) +{ + vkb_cbtrack *k = cur(); + vkb_srv_table *t = vkb_srv_current_table(); + for (uint32_t i = 0; i < n; i++) + for (uint32_t j = 0; j < s[i].commandBufferCount; j++) + if (!vkb_cbtrack_known(t, s[i].pCommandBuffers[j], "vkQueueSubmit")) return VK_ERROR_DEVICE_LOST; + return k->submit(queue, n, s, fence); +} + +static VKAPI_ATTR VkResult VKAPI_CALL cbt_QueueSubmit2(VkQueue queue, uint32_t n, const VkSubmitInfo2 *s, VkFence fence) +{ + vkb_cbtrack *k = cur(); + vkb_srv_table *t = vkb_srv_current_table(); + for (uint32_t i = 0; i < n; i++) + for (uint32_t j = 0; j < s[i].commandBufferInfoCount; j++) + if (!vkb_cbtrack_known(t, s[i].pCommandBufferInfos[j].commandBuffer, "vkQueueSubmit2")) return VK_ERROR_DEVICE_LOST; + return k->submit2(queue, n, s, fence); +} + +void vkb_cbtrack_device_init(vkb_srv_table *t) +{ + if (!t->dt.vkAllocateCommandBuffers || !t->dt.vkFreeCommandBuffers || !t->dt.vkDestroyCommandPool || !t->dt.vkBeginCommandBuffer || + !t->dt.vkQueueSubmit) + return; + vkb_cbtrack *k = calloc(1, sizeof(*k)); + if (!k) return; + pthread_mutex_init(&k->lock, NULL); + k->alloc = t->dt.vkAllocateCommandBuffers; + k->free_cbs = t->dt.vkFreeCommandBuffers; + k->destroy_pool = t->dt.vkDestroyCommandPool; + k->begin = t->dt.vkBeginCommandBuffer; + k->submit = t->dt.vkQueueSubmit; + k->submit2 = t->dt.vkQueueSubmit2; + t->cbtrack = k; + t->dt.vkAllocateCommandBuffers = cbt_AllocateCommandBuffers; + t->dt.vkFreeCommandBuffers = cbt_FreeCommandBuffers; + t->dt.vkDestroyCommandPool = cbt_DestroyCommandPool; + t->dt.vkBeginCommandBuffer = cbt_BeginCommandBuffer; + t->dt.vkQueueSubmit = cbt_QueueSubmit; + if (k->submit2) t->dt.vkQueueSubmit2 = cbt_QueueSubmit2; +} + +void vkb_cbtrack_device_destroy(vkb_srv_table *t) +{ + vkb_cbtrack *k = t->cbtrack; + if (!k) return; + for (unsigned h = 0; h < CBT_BUCKETS; h++) { + while (k->b[h]) { + cbt_entry *e = k->b[h]; + k->b[h] = e->next; + free(e); + } + } + pthread_mutex_destroy(&k->lock); + free(k); + t->cbtrack = NULL; +} diff --git a/app/src/main/cpp/vkbridge/server_conn.c b/app/src/main/cpp/vkbridge/server_conn.c index bebcaa3d..6f66ba45 100644 --- a/app/src/main/cpp/vkbridge/server_conn.c +++ b/app/src/main/cpp/vkbridge/server_conn.c @@ -8,6 +8,8 @@ #include "vkb_server.h" #include +#include +#include #include #include #include @@ -102,8 +104,14 @@ void vkb_proc_cleanup(vkb_srv_proc *p) if (mine) tables[i] = NULL; pthread_mutex_unlock(&tables_lock); if (!mine) continue; - if (t->is_device) { + if (p->driver_crashed) { + /* The driver faulted for this process: calling into it for its objects again could + * fault again, outside any guard. They are left to the driver. */ + VKB_WARN("process %d: %s %u left as it is (the driver crashed serving it)", p->pid, t->is_device ? "device" : "instance", t->id); + if (t->is_device) vkb_cbtrack_device_destroy(t); + } else if (t->is_device) { VKB_INFO("process %d exited without destroying device %u; cleaning up", p->pid, t->id); + vkb_cbtrack_device_destroy(t); if (t->real.vkDeviceWaitIdle) t->real.vkDeviceWaitIdle(t->device); vkb_spvfix_device_destroy(t); vkb_dsmap_device_destroy(t); @@ -218,6 +226,12 @@ void vkb_sv_vkEndCommandBuffer(vkb_srv_call *c) vkb_srv_bad_message(c); return; } + /* A command buffer the device never handed out (or has freed) is not replayed: the driver would + * be given a pointer it does not own (server_cbtrack.c). */ + if (!vkb_cbtrack_known(c->table, cb, "vkEndCommandBuffer")) { + vkb_enc_u32(&c->r, (uint32_t)VK_ERROR_DEVICE_LOST); + return; + } VkResult begin_r = VK_SUCCESS; size_t pos = 0; vkb_enc scratch; @@ -425,8 +439,55 @@ static int srv_recv(vkb_srv_conn *conn, vkb_msg_hdr *h, uint8_t **payload, int * } } +/* ------------------------------------------------------------------ driver fault guard */ + +/* + * A fault inside the GPU driver while serving one client used to take the whole app down (the + * server runs in its process). Each request runs under a guard: SIGSEGV/SIGBUS on that thread + * jumps back here, the client is cut off (it sees the bridge gone and fails or falls back), and + * the app and the other clients go on. Any other fault goes to the handler that was there before. + */ +static __thread sigjmp_buf *t_guard; +static __thread volatile sig_atomic_t t_fault; +static struct sigaction prev_segv, prev_bus; +static pthread_once_t guard_once = PTHREAD_ONCE_INIT; + +static void guard_handler(int sig, siginfo_t *si, void *uc) +{ + sigjmp_buf *g = t_guard; + if (g) { + t_guard = NULL; + t_fault = sig; + siglongjmp(*g, 1); + } + struct sigaction *p = sig == SIGBUS ? &prev_bus : &prev_segv; + if ((p->sa_flags & SA_SIGINFO) && p->sa_sigaction) { + p->sa_sigaction(sig, si, uc); + return; + } + if (!(p->sa_flags & SA_SIGINFO) && p->sa_handler == SIG_IGN) return; + if (!(p->sa_flags & SA_SIGINFO) && p->sa_handler && p->sa_handler != SIG_DFL) { + p->sa_handler(sig); + return; + } + signal(sig, SIG_DFL); + raise(sig); +} + +static void guard_install(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = guard_handler; + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, &prev_segv); + sigaction(SIGBUS, &sa, &prev_bus); +} + static void *conn_main(void *arg) { + pthread_once(&guard_once, guard_install); vkb_srv_conn *conn = arg; vkb_srv_call c; vkb_enc_init(&c.r); @@ -467,7 +528,19 @@ static void *conn_main(void *arg) vkb_srv_set_current_table(c.table); if (vkb_verbose) VKB_DBG("-> %s", vkb_cmd_names[h.cmd]); vkb_srv_current_cmd = vkb_cmd_names[h.cmd]; + sigjmp_buf jb; + if (sigsetjmp(jb, 1)) { + VKB_ERR("the GPU driver crashed (signal %d) in %s for process %d; that client is cut off, the app and " + "the other clients carry on", (int)t_fault, vkb_cmd_names[h.cmd], conn->proc ? conn->proc->pid : -1); + if (conn->proc) conn->proc->driver_crashed = 1; + vkb_srv_current_cmd = NULL; + vkb_srv_set_current_table(NULL); + order_done(conn->proc, h.seq); + break; + } + t_guard = &jb; vkb_srv_handlers[h.cmd](&c); + t_guard = NULL; vkb_srv_current_cmd = NULL; vkb_srv_set_current_table(NULL); if (st_period) stats_add(h.cmd, mono_ns() - t1, t1 - t0); diff --git a/app/src/main/cpp/vkbridge/server_instance.c b/app/src/main/cpp/vkbridge/server_instance.c index 6b6c25db..0cbcfd12 100644 --- a/app/src/main/cpp/vkbridge/server_instance.c +++ b/app/src/main/cpp/vkbridge/server_instance.c @@ -319,6 +319,7 @@ void vkb_sv_vkCreateDevice(vkb_srv_call *c) vkb_pcache_device_init(t); vkb_dsmap_device_init(t); vkb_spvfix_device_init(t); + vkb_cbtrack_device_init(t); /* last: it wraps what the others installed */ VKB_INFO("device %u created on %s (%u extensions, memory sharing %s)", id, k ? k->name : "?", n, vkb_mem_strategy_name(strategy)); for (uint32_t i = 0; i < n; i++) VKB_DBG(" device extension %s", exts[i]); @@ -341,6 +342,7 @@ void vkb_sv_vkDestroyDevice(vkb_srv_call *c) return; } vkb_table_unregister(t); + vkb_cbtrack_device_destroy(t); vkb_spvfix_device_destroy(t); vkb_dsmap_device_destroy(t); vkb_pcache_device_destroy(t); diff --git a/app/src/main/cpp/vkbridge/server_pcache.c b/app/src/main/cpp/vkbridge/server_pcache.c index 9db19947..8ae55a59 100644 --- a/app/src/main/cpp/vkbridge/server_pcache.c +++ b/app/src/main/cpp/vkbridge/server_pcache.c @@ -46,7 +46,11 @@ void vkb_pcache_set_dir(const char *dir) { if (!dir || !*dir) return; mkdir(dir, 0700); - pc_dir = dir; + /* A copy: the JNI entry frees its string once the server is up, and the pointer kept before + * printed "pipeline cache \xef\xbf\xbdRYp/13b5-..." and wrote the cache under that name. */ + char *copy = strdup(dir); + if (!copy) return; + pc_dir = copy; VKB_INFO("pipeline cache directory %s", dir); } diff --git a/app/src/main/cpp/vkbridge/vkb_server.h b/app/src/main/cpp/vkbridge/vkb_server.h index bab8b1e9..edb8c6db 100644 --- a/app/src/main/cpp/vkbridge/vkb_server.h +++ b/app/src/main/cpp/vkbridge/vkb_server.h @@ -33,6 +33,7 @@ typedef struct vkb_srv_proc { uint64_t watermark; /* every async seq <= this has run */ uint8_t done[VKB_SEQ_RING]; /* seqs above the watermark that have run */ int conn_closed; /* a connection ended (a seq may never arrive) */ + int driver_crashed; /* the driver faulted serving it: its objects are left alone */ } vkb_srv_proc; typedef struct vkb_srv_conn { @@ -88,6 +89,7 @@ typedef struct vkb_srv_table { struct vkb_pcache *pcache; /* server_pcache.c */ struct vkb_dsmap *dsmap; /* server_dsmap.c */ struct vkb_spvfix *spvfix; /* server_spvfix.c */ + struct vkb_cbtrack *cbtrack; /* server_cbtrack.c */ } vkb_srv_table; /* server_main.c */ @@ -97,6 +99,11 @@ extern int vkb_verbose; void vkb_log_summary(void); +/* server_cbtrack.c: command buffers the device handed out; others are refused, not passed on. */ +void vkb_cbtrack_device_init(vkb_srv_table *t); +void vkb_cbtrack_device_destroy(vkb_srv_table *t); +int vkb_cbtrack_known(vkb_srv_table *t, VkCommandBuffer cb, const char *what); + /* server_spvfix.c */ void vkb_spvfix_device_init(vkb_srv_table *t); void vkb_spvfix_device_destroy(vkb_srv_table *t); diff --git a/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt b/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt index 4b03b882..14ef8312 100644 --- a/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt +++ b/app/src/main/java/com/droiddeck/launcher/gpu/GpuInfo.kt @@ -86,7 +86,7 @@ data class GpuInfo( fun supportText(context: Context): String = context.getString(when (support) { Support.TESTED -> R.string.gpuinfo_supported Support.UNTESTED -> when (family) { - Family.POWERVR -> R.string.gpuinfo_powervr + Family.POWERVR -> if (name.contains("Xclipse", ignoreCase = true)) R.string.gpuinfo_xclipse else R.string.gpuinfo_powervr Family.A7XX_LOW -> R.string.gpuinfo_experimental Family.MALI_JM, Family.MALI_G610_G710, Family.MALI_G615, Family.MALI_G720, Family.MALI_LATEST, Family.MALI_OTHER -> R.string.gpuinfo_experimental else -> R.string.gpuinfo_below_tested diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 48359f68..943dea74 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1908,6 +1908,7 @@ Not an Adreno GPU System PowerVR driver: compatibility varies by device + Experimental: RADV or the system Xclipse driver System PowerVR Uses the built-in drivers for this GPU The system PowerVR driver is selected for the runtime and display. diff --git a/tools/linuxfs/overlay/usr/local/bin/droiddeck-session b/tools/linuxfs/overlay/usr/local/bin/droiddeck-session index 9d92f919..94e9b575 100755 --- a/tools/linuxfs/overlay/usr/local/bin/droiddeck-session +++ b/tools/linuxfs/overlay/usr/local/bin/droiddeck-session @@ -1190,6 +1190,10 @@ BUSCONF # The loader's view first, then the variants that change what Zink asks of the device; # the first that starts is what the client gets. VK_LOADER_DEBUG=error,warn timeout 20 /usr/local/bin/droiddeck-glprobe 2>&1 | grep -v drirc | head -40 | sed 's/^/== glprobe (loader): /' + # What Zink asked the device before it gave up (the Mali compatibility layer's trace), Mesa's + # own debug output, and the device as Vulkan programs see it under the same environment. + DROIDDECK_MALI_COMPAT_TRACE=1 MESA_DEBUG=1 LIBGL_DEBUG=verbose timeout 20 /usr/local/bin/droiddeck-glprobe 2>&1 | grep -v drirc | head -80 | sed 's/^/== glprobe (trace): /' + command -v vulkaninfo >/dev/null 2>&1 && DROIDDECK_MALI_COMPAT_TRACE=1 timeout 20 vulkaninfo --summary 2>&1 | head -40 | sed 's/^/== glprobe (vulkaninfo): /' bl_gl_ok= for bl_variant in "ZINK_DEBUG=" "ZINK_DEBUG= ZINK_DESCRIPTORS=auto" "ZINK_DEBUG= LIBGL_KOPPER_DISABLE=true" "ZINK_DEBUG= DROIDDECK_MALI_COMPAT=0"; do if env $bl_variant timeout 20 /usr/local/bin/droiddeck-glprobe 2>&1 | grep -v drirc | head -20 | sed "s/^/== glprobe ($bl_variant): /"; [ "${PIPESTATUS[0]}" = 0 ]; then diff --git a/tools/mangoapp/mangoapp b/tools/mangoapp/mangoapp index e912be1a..b6e188fb 100644 --- a/tools/mangoapp/mangoapp +++ b/tools/mangoapp/mangoapp @@ -18,4 +18,11 @@ if [ "$(wc -l < "$starts")" -gt 5 ]; then echo "mangoapp: started $(wc -l < "$starts") times in 60 s; the performance overlay stays off for this session" >&2 exec sleep infinity fi +# The system PowerVR route (BL_POWERVR_RENDER_NODE): Zink could not make mangoapp a swapchain there +# ("zink: could not create swapchain", then GLXBadCurrentWindow) and it crashed at every start. The +# overlay is a few text lines: llvmpipe draws it. +if [ -n "${BL_POWERVR_RENDER_NODE:-}" ]; then + export GALLIUM_DRIVER=llvmpipe LIBGL_ALWAYS_SOFTWARE=1 + unset MESA_LOADER_DRIVER_OVERRIDE LIBGL_KOPPER_DRI2 +fi LD_LIBRARY_PATH=/usr/local/lib/mangoapp${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} exec /usr/local/lib/mangoapp/mangoapp "$@" diff --git a/tools/venus/layer/layer.c b/tools/venus/layer/layer.c index cded9433..fbb973f0 100644 --- a/tools/venus/layer/layer.c +++ b/tools/venus/layer/layer.c @@ -151,6 +151,18 @@ static bool wanted(void) return getenv("WINEPREFIX") != NULL; } +/* DROIDDECK_MALI_COMPAT_TRACE=1: what an application asks before it creates a device (Zink on a + * Mali-G615 through Venus gave up before vkCreateDevice without a word). */ +static bool tracing(void) +{ + static int on = -1; + if (on < 0) { + const char *v = getenv("DROIDDECK_MALI_COMPAT_TRACE"); + on = v && *v && strcmp(v, "0") != 0; + } + return on; +} + static bool has_extension(struct instance *inst, VkPhysicalDevice physical_device, const char *name) { uint32_t count = 0; @@ -337,6 +349,13 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceFeatures2(VkPhysicalDevice ph inst->GetPhysicalDeviceFeatures2(physical_device, features); relink(dropped, dropped_count); report_features(&features->features, gaps); + if (tracing()) { + char chain[512] = ""; + size_t used = 0; + for (VkBaseOutStructure *s = features->pNext; s && used + 16 < sizeof(chain); s = s->pNext) + used += (size_t)snprintf(chain + used, sizeof(chain) - used, " %d", (int)s->sType); + layer_log("trace: vkGetPhysicalDeviceFeatures2 (gaps 0x%x, %u structures held back), chain:%s", gaps, dropped_count, chain); + } for (VkBaseOutStructure *s = features->pNext; s; s = s->pNext) { if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT && (gaps & GAP_ROBUSTNESS2)) { ((VkPhysicalDeviceRobustness2FeaturesEXT *)s)->robustBufferAccess2 = VK_TRUE; @@ -362,6 +381,10 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceProperties2(VkPhysicalDevice struct instance *inst = instance_from_handle(physical_device); inst->GetPhysicalDeviceProperties2(physical_device, properties); report_limits(&properties->properties.limits, physical_device_gaps(inst, physical_device)); + if (tracing()) + layer_log("trace: vkGetPhysicalDeviceProperties2: %s, Vulkan %u.%u.%u, type %d", properties->properties.deviceName, + VK_API_VERSION_MAJOR(properties->properties.apiVersion), VK_API_VERSION_MINOR(properties->properties.apiVersion), + VK_API_VERSION_PATCH(properties->properties.apiVersion), (int)properties->properties.deviceType); } /* The extensions the layer stands in for, added to the driver's list. */ @@ -391,6 +414,8 @@ static VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysi inst->EnumerateDeviceExtensionProperties(physical_device, NULL, &own, all); memcpy(all + own, extra, extra_count * sizeof(*extra)); uint32_t total = own + extra_count; + if (tracing()) + layer_log("trace: vkEnumerateDeviceExtensionProperties: %u from the driver, %u added by the layer", own, extra_count); uint32_t n = *count < total ? *count : total; memcpy(properties, all, n * sizeof(*all)); free(all); @@ -411,6 +436,10 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo PFN_vkCreateInstance create = (PFN_vkCreateInstance)gipa(VK_NULL_HANDLE, "vkCreateInstance"); VkResult result = create(info, allocator, instance); + if (tracing()) + layer_log("trace: vkCreateInstance for %s (API %u.%u): %d, layer %s", info->pApplicationInfo && info->pApplicationInfo->pApplicationName ? + info->pApplicationInfo->pApplicationName : "?", info->pApplicationInfo ? VK_API_VERSION_MAJOR(info->pApplicationInfo->apiVersion) : 0, + info->pApplicationInfo ? VK_API_VERSION_MINOR(info->pApplicationInfo->apiVersion) : 0, (int)result, wanted() ? "active" : "idle"); if (result != VK_SUCCESS) return result;