mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
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.
This commit is contained in:
1 parent
ab54536886
commit
e40cd36151
10 files changed
+324
-3
No files matched your search
@@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
#include "vkb_server.h"
|
#include "vkb_server.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -102,8 +104,14 @@ void vkb_proc_cleanup(vkb_srv_proc *p)
|
|||||||
if (mine) tables[i] = NULL;
|
if (mine) tables[i] = NULL;
|
||||||
pthread_mutex_unlock(&tables_lock);
|
pthread_mutex_unlock(&tables_lock);
|
||||||
if (!mine) continue;
|
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_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);
|
if (t->real.vkDeviceWaitIdle) t->real.vkDeviceWaitIdle(t->device);
|
||||||
vkb_spvfix_device_destroy(t);
|
vkb_spvfix_device_destroy(t);
|
||||||
vkb_dsmap_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);
|
vkb_srv_bad_message(c);
|
||||||
return;
|
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;
|
VkResult begin_r = VK_SUCCESS;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
vkb_enc scratch;
|
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)
|
static void *conn_main(void *arg)
|
||||||
{
|
{
|
||||||
|
pthread_once(&guard_once, guard_install);
|
||||||
vkb_srv_conn *conn = arg;
|
vkb_srv_conn *conn = arg;
|
||||||
vkb_srv_call c;
|
vkb_srv_call c;
|
||||||
vkb_enc_init(&c.r);
|
vkb_enc_init(&c.r);
|
||||||
@@ -467,7 +528,19 @@ static void *conn_main(void *arg)
|
|||||||
vkb_srv_set_current_table(c.table);
|
vkb_srv_set_current_table(c.table);
|
||||||
if (vkb_verbose) VKB_DBG("-> %s", vkb_cmd_names[h.cmd]);
|
if (vkb_verbose) VKB_DBG("-> %s", vkb_cmd_names[h.cmd]);
|
||||||
vkb_srv_current_cmd = 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);
|
vkb_srv_handlers[h.cmd](&c);
|
||||||
|
t_guard = NULL;
|
||||||
vkb_srv_current_cmd = NULL;
|
vkb_srv_current_cmd = NULL;
|
||||||
vkb_srv_set_current_table(NULL);
|
vkb_srv_set_current_table(NULL);
|
||||||
if (st_period) stats_add(h.cmd, mono_ns() - t1, t1 - t0);
|
if (st_period) stats_add(h.cmd, mono_ns() - t1, t1 - t0);
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ void vkb_sv_vkCreateDevice(vkb_srv_call *c)
|
|||||||
vkb_pcache_device_init(t);
|
vkb_pcache_device_init(t);
|
||||||
vkb_dsmap_device_init(t);
|
vkb_dsmap_device_init(t);
|
||||||
vkb_spvfix_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_INFO("device %u created on %s (%u extensions, memory sharing %s)", id, k ? k->name : "?", n,
|
||||||
vkb_mem_strategy_name(strategy));
|
vkb_mem_strategy_name(strategy));
|
||||||
for (uint32_t i = 0; i < n; i++) VKB_DBG(" device extension %s", exts[i]);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
vkb_table_unregister(t);
|
vkb_table_unregister(t);
|
||||||
|
vkb_cbtrack_device_destroy(t);
|
||||||
vkb_spvfix_device_destroy(t);
|
vkb_spvfix_device_destroy(t);
|
||||||
vkb_dsmap_device_destroy(t);
|
vkb_dsmap_device_destroy(t);
|
||||||
vkb_pcache_device_destroy(t);
|
vkb_pcache_device_destroy(t);
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ void vkb_pcache_set_dir(const char *dir)
|
|||||||
{
|
{
|
||||||
if (!dir || !*dir) return;
|
if (!dir || !*dir) return;
|
||||||
mkdir(dir, 0700);
|
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);
|
VKB_INFO("pipeline cache directory %s", dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ typedef struct vkb_srv_proc {
|
|||||||
uint64_t watermark; /* every async seq <= this has run */
|
uint64_t watermark; /* every async seq <= this has run */
|
||||||
uint8_t done[VKB_SEQ_RING]; /* seqs above the watermark that have 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 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;
|
} vkb_srv_proc;
|
||||||
|
|
||||||
typedef struct vkb_srv_conn {
|
typedef struct vkb_srv_conn {
|
||||||
@@ -88,6 +89,7 @@ typedef struct vkb_srv_table {
|
|||||||
struct vkb_pcache *pcache; /* server_pcache.c */
|
struct vkb_pcache *pcache; /* server_pcache.c */
|
||||||
struct vkb_dsmap *dsmap; /* server_dsmap.c */
|
struct vkb_dsmap *dsmap; /* server_dsmap.c */
|
||||||
struct vkb_spvfix *spvfix; /* server_spvfix.c */
|
struct vkb_spvfix *spvfix; /* server_spvfix.c */
|
||||||
|
struct vkb_cbtrack *cbtrack; /* server_cbtrack.c */
|
||||||
} vkb_srv_table;
|
} vkb_srv_table;
|
||||||
|
|
||||||
/* server_main.c */
|
/* server_main.c */
|
||||||
@@ -97,6 +99,11 @@ extern int vkb_verbose;
|
|||||||
|
|
||||||
void vkb_log_summary(void);
|
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 */
|
/* server_spvfix.c */
|
||||||
void vkb_spvfix_device_init(vkb_srv_table *t);
|
void vkb_spvfix_device_init(vkb_srv_table *t);
|
||||||
void vkb_spvfix_device_destroy(vkb_srv_table *t);
|
void vkb_spvfix_device_destroy(vkb_srv_table *t);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ data class GpuInfo(
|
|||||||
fun supportText(context: Context): String = context.getString(when (support) {
|
fun supportText(context: Context): String = context.getString(when (support) {
|
||||||
Support.TESTED -> R.string.gpuinfo_supported
|
Support.TESTED -> R.string.gpuinfo_supported
|
||||||
Support.UNTESTED -> when (family) {
|
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.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
|
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
|
else -> R.string.gpuinfo_below_tested
|
||||||
|
|||||||
@@ -1908,6 +1908,7 @@
|
|||||||
<!-- GPU support line -->
|
<!-- GPU support line -->
|
||||||
<string name="gpuinfo_not_adreno">Not an Adreno GPU</string>
|
<string name="gpuinfo_not_adreno">Not an Adreno GPU</string>
|
||||||
<string name="gpuinfo_powervr">System PowerVR driver: compatibility varies by device</string>
|
<string name="gpuinfo_powervr">System PowerVR driver: compatibility varies by device</string>
|
||||||
|
<string name="gpuinfo_xclipse">Experimental: RADV or the system Xclipse driver</string>
|
||||||
<string name="gpu_system_powervr">System PowerVR</string>
|
<string name="gpu_system_powervr">System PowerVR</string>
|
||||||
<string name="gpu_auto_system_hint">Uses the built-in drivers for this GPU</string>
|
<string name="gpu_auto_system_hint">Uses the built-in drivers for this GPU</string>
|
||||||
<string name="drivers_powervr_builtin">The system PowerVR driver is selected for the runtime and display.</string>
|
<string name="drivers_powervr_builtin">The system PowerVR driver is selected for the runtime and display.</string>
|
||||||
|
|||||||
@@ -1190,6 +1190,10 @@ BUSCONF
|
|||||||
# The loader's view first, then the variants that change what Zink asks of the device;
|
# 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.
|
# 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): /'
|
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=
|
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
|
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
|
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
|
||||||
|
|||||||
@@ -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
|
echo "mangoapp: started $(wc -l < "$starts") times in 60 s; the performance overlay stays off for this session" >&2
|
||||||
exec sleep infinity
|
exec sleep infinity
|
||||||
fi
|
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 "$@"
|
LD_LIBRARY_PATH=/usr/local/lib/mangoapp${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} exec /usr/local/lib/mangoapp/mangoapp "$@"
|
||||||
@@ -151,6 +151,18 @@ static bool wanted(void)
|
|||||||
return getenv("WINEPREFIX") != NULL;
|
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)
|
static bool has_extension(struct instance *inst, VkPhysicalDevice physical_device, const char *name)
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
@@ -337,6 +349,13 @@ static VKAPI_ATTR void VKAPI_CALL GetPhysicalDeviceFeatures2(VkPhysicalDevice ph
|
|||||||
inst->GetPhysicalDeviceFeatures2(physical_device, features);
|
inst->GetPhysicalDeviceFeatures2(physical_device, features);
|
||||||
relink(dropped, dropped_count);
|
relink(dropped, dropped_count);
|
||||||
report_features(&features->features, gaps);
|
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) {
|
for (VkBaseOutStructure *s = features->pNext; s; s = s->pNext) {
|
||||||
if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT && (gaps & GAP_ROBUSTNESS2)) {
|
if (s->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT && (gaps & GAP_ROBUSTNESS2)) {
|
||||||
((VkPhysicalDeviceRobustness2FeaturesEXT *)s)->robustBufferAccess2 = VK_TRUE;
|
((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);
|
struct instance *inst = instance_from_handle(physical_device);
|
||||||
inst->GetPhysicalDeviceProperties2(physical_device, properties);
|
inst->GetPhysicalDeviceProperties2(physical_device, properties);
|
||||||
report_limits(&properties->properties.limits, physical_device_gaps(inst, physical_device));
|
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. */
|
/* 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);
|
inst->EnumerateDeviceExtensionProperties(physical_device, NULL, &own, all);
|
||||||
memcpy(all + own, extra, extra_count * sizeof(*extra));
|
memcpy(all + own, extra, extra_count * sizeof(*extra));
|
||||||
uint32_t total = own + extra_count;
|
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;
|
uint32_t n = *count < total ? *count : total;
|
||||||
memcpy(properties, all, n * sizeof(*all));
|
memcpy(properties, all, n * sizeof(*all));
|
||||||
free(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");
|
PFN_vkCreateInstance create = (PFN_vkCreateInstance)gipa(VK_NULL_HANDLE, "vkCreateInstance");
|
||||||
VkResult result = create(info, allocator, instance);
|
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)
|
if (result != VK_SUCCESS)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user