mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-07 10:46:06 -10:00
Non-Adreno round 8: bridge sharing fallback and crash detail, Venus diagnostics and 1.2 gate, compositor disconnect reasons, RADV upstream
- Mali bridge: the self-test fails "memory sharing" when the server offers no host-visible memory type (no sharing method passed, RP4 Pro G77); the session then falls back to the PanVK pair like a failed draw, and Setup says the bridge cannot share memory with that driver. Without a pair to fall back on, the log says so. - Mali bridge: a guarded driver crash now logs the sub-command it was in, the fault address and pc (library + offset via dladdr), the command buffer's level, begin flags, pool flags and state, and the last 32 replayed commands. Begin on an executable buffer from a pool without RESET_COMMAND_BUFFER_BIT is logged once (implicit reset). - Venus: vulkan-summary.txt gets the device extension list and key features (full vulkaninfo in vulkan-full.txt); a failed GL probe runs once with VN_DEBUG=init,result, ZINK_DEBUG=mem, MESA_LOG_LEVEL=debug and the layer trace into glprobe-trace.txt. The Mali compatibility layer's trace covers every pre-device query (devices, memory, queue families, formats, image formats, external handles) and names the extensions. - Venus: off when the phone's driver reports Vulkan below 1.2 (gamescope skips such a device); Setup and the graphics report say why and the session uses the PanVK pair. - Compositor: libwayland's own disconnect reasons go to wayland.log, and a client's buffer may grow to 1 MB before a slow reader is dropped. A session that ended without teardown records Android's exit reasons. - Drivers: RADV-Xclipse upstream (radv-xclipse-upstream) is a manual choice for Xclipse; Auto keeps radv-xclipse.
This commit is contained in:
1 parent
521d666046
commit
a4d47cf33e
21 files changed
+646
-25
No files matched your search
@@ -10,21 +10,41 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "vkb_server.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CBT_BUCKETS 1024
|
||||
|
||||
enum { CBT_INITIAL, CBT_RECORDING, CBT_EXECUTABLE };
|
||||
|
||||
typedef struct cbt_entry {
|
||||
VkCommandBuffer cb;
|
||||
VkCommandPool pool;
|
||||
VkCommandBufferLevel level;
|
||||
int state;
|
||||
VkCommandBufferUsageFlags begin_flags;
|
||||
int inheritance;
|
||||
uint32_t begins;
|
||||
struct cbt_entry *next;
|
||||
} cbt_entry;
|
||||
|
||||
typedef struct cbt_pool {
|
||||
VkCommandPool pool;
|
||||
VkCommandPoolCreateFlags flags;
|
||||
uint32_t qfam;
|
||||
struct cbt_pool *next;
|
||||
} cbt_pool;
|
||||
|
||||
typedef struct vkb_cbtrack {
|
||||
pthread_mutex_t lock;
|
||||
cbt_entry *b[CBT_BUCKETS];
|
||||
VkCommandBuffer last_refused;
|
||||
cbt_pool *pools;
|
||||
int warned_implicit_reset;
|
||||
PFN_vkCreateCommandPool create_pool;
|
||||
PFN_vkResetCommandPool reset_pool;
|
||||
PFN_vkResetCommandBuffer reset_cb;
|
||||
PFN_vkAllocateCommandBuffers alloc;
|
||||
PFN_vkFreeCommandBuffers free_cbs;
|
||||
PFN_vkDestroyCommandPool destroy_pool;
|
||||
@@ -47,11 +67,55 @@ static vkb_cbtrack *cur(void)
|
||||
return t ? t->cbtrack : NULL;
|
||||
}
|
||||
|
||||
static int known_locked(vkb_cbtrack *k, VkCommandBuffer cb)
|
||||
static cbt_entry *find_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;
|
||||
if (e->cb == cb) return e;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int known_locked(vkb_cbtrack *k, VkCommandBuffer cb) { return find_locked(k, cb) != NULL; }
|
||||
|
||||
static cbt_pool *pool_locked(vkb_cbtrack *k, VkCommandPool pool)
|
||||
{
|
||||
for (cbt_pool *p = k->pools; p; p = p->next)
|
||||
if (p->pool == pool) return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *state_name(int s) { return s == CBT_RECORDING ? "recording" : s == CBT_EXECUTABLE ? "executable" : "initial"; }
|
||||
|
||||
void vkb_cbtrack_describe(vkb_srv_table *t, VkCommandBuffer cb, char *out, size_t n)
|
||||
{
|
||||
vkb_cbtrack *k = t ? t->cbtrack : NULL;
|
||||
if (!k) {
|
||||
snprintf(out, n, "command buffer %p (not tracked)", (void *)cb);
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&k->lock);
|
||||
cbt_entry *e = find_locked(k, cb);
|
||||
cbt_pool *p = e ? pool_locked(k, e->pool) : NULL;
|
||||
if (!e)
|
||||
snprintf(out, n, "command buffer %p: unknown to this device", (void *)cb);
|
||||
else
|
||||
snprintf(out, n, "command buffer %p: level %s, state %s, begin #%u flags 0x%x%s; pool %p flags %s (queue family %d)",
|
||||
(void *)cb, e->level == VK_COMMAND_BUFFER_LEVEL_SECONDARY ? "secondary" : "primary", state_name(e->state),
|
||||
e->begins, (unsigned)e->begin_flags, e->inheritance ? " with inheritance info" : "", (void *)e->pool,
|
||||
p ? (p->flags & VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT ? "RESET_COMMAND_BUFFER" : "0") : "unknown",
|
||||
p ? (int)p->qfam : -1);
|
||||
if (p && (p->flags & ~VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT))
|
||||
snprintf(out + strlen(out), n - strlen(out), " +0x%x", (unsigned)(p->flags & ~VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
}
|
||||
|
||||
void vkb_cbtrack_ended(vkb_srv_table *t, VkCommandBuffer cb)
|
||||
{
|
||||
vkb_cbtrack *k = t ? t->cbtrack : NULL;
|
||||
if (!k) return;
|
||||
pthread_mutex_lock(&k->lock);
|
||||
cbt_entry *e = find_locked(k, cb);
|
||||
if (e) e->state = CBT_EXECUTABLE;
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
}
|
||||
|
||||
int vkb_cbtrack_known(vkb_srv_table *t, VkCommandBuffer cb, const char *what)
|
||||
@@ -80,8 +144,10 @@ static VKAPI_ATTR VkResult VKAPI_CALL cbt_AllocateCommandBuffers(VkDevice device
|
||||
if (!out[i] || known_locked(k, out[i])) continue;
|
||||
cbt_entry *e = malloc(sizeof(*e));
|
||||
if (!e) continue;
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->cb = out[i];
|
||||
e->pool = info->commandPool;
|
||||
e->level = info->level;
|
||||
unsigned h = bucket(out[i]);
|
||||
e->next = k->b[h];
|
||||
k->b[h] = e;
|
||||
@@ -122,15 +188,82 @@ static VKAPI_ATTR void VKAPI_CALL cbt_DestroyCommandPool(VkDevice device, VkComm
|
||||
if (pool) {
|
||||
pthread_mutex_lock(&k->lock);
|
||||
forget_locked(k, VK_NULL_HANDLE, pool);
|
||||
for (cbt_pool **pp = &k->pools; *pp; pp = &(*pp)->next) {
|
||||
if ((*pp)->pool == pool) {
|
||||
cbt_pool *p = *pp;
|
||||
*pp = p->next;
|
||||
free(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
}
|
||||
k->destroy_pool(device, pool, a);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL cbt_CreateCommandPool(VkDevice device, const VkCommandPoolCreateInfo *info,
|
||||
const VkAllocationCallbacks *a, VkCommandPool *out)
|
||||
{
|
||||
vkb_cbtrack *k = cur();
|
||||
VkResult r = k->create_pool(device, info, a, out);
|
||||
if (r != VK_SUCCESS || !info || !out) return r;
|
||||
cbt_pool *p = calloc(1, sizeof(*p));
|
||||
if (!p) return r;
|
||||
p->pool = *out;
|
||||
p->flags = info->flags;
|
||||
p->qfam = info->queueFamilyIndex;
|
||||
pthread_mutex_lock(&k->lock);
|
||||
p->next = k->pools;
|
||||
k->pools = p;
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
return r;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL cbt_ResetCommandPool(VkDevice device, VkCommandPool pool, VkCommandPoolResetFlags flags)
|
||||
{
|
||||
vkb_cbtrack *k = cur();
|
||||
pthread_mutex_lock(&k->lock);
|
||||
for (unsigned h = 0; h < CBT_BUCKETS; h++)
|
||||
for (cbt_entry *e = k->b[h]; e; e = e->next)
|
||||
if (e->pool == pool) e->state = CBT_INITIAL;
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
return k->reset_pool(device, pool, flags);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL cbt_ResetCommandBuffer(VkCommandBuffer cb, VkCommandBufferResetFlags flags)
|
||||
{
|
||||
vkb_cbtrack *k = cur();
|
||||
if (!vkb_cbtrack_known(vkb_srv_current_table(), cb, "vkResetCommandBuffer")) return VK_ERROR_DEVICE_LOST;
|
||||
pthread_mutex_lock(&k->lock);
|
||||
cbt_entry *e = find_locked(k, cb);
|
||||
if (e) e->state = CBT_INITIAL;
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
return k->reset_cb(cb, flags);
|
||||
}
|
||||
|
||||
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;
|
||||
vkb_srv_table *t = vkb_srv_current_table();
|
||||
if (!vkb_cbtrack_known(t, cb, "vkBeginCommandBuffer")) return VK_ERROR_DEVICE_LOST;
|
||||
pthread_mutex_lock(&k->lock);
|
||||
cbt_entry *e = find_locked(k, cb);
|
||||
int bad_reset = 0, prev = CBT_INITIAL;
|
||||
if (e) {
|
||||
cbt_pool *p = pool_locked(k, e->pool);
|
||||
prev = e->state;
|
||||
bad_reset = prev != CBT_INITIAL && p && !(p->flags & VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) && !k->warned_implicit_reset;
|
||||
if (bad_reset) k->warned_implicit_reset = 1;
|
||||
e->state = CBT_RECORDING;
|
||||
e->begins++;
|
||||
e->begin_flags = info ? info->flags : 0;
|
||||
e->inheritance = info && info->pInheritanceInfo;
|
||||
}
|
||||
pthread_mutex_unlock(&k->lock);
|
||||
if (bad_reset)
|
||||
VKB_WARN("device %u: vkBeginCommandBuffer on command buffer %p in the %s state, from a pool without "
|
||||
"RESET_COMMAND_BUFFER_BIT (an implicit reset the spec does not allow; logged once)", t ? t->id : 0, (void *)cb,
|
||||
state_name(prev));
|
||||
return k->begin(cb, info);
|
||||
}
|
||||
|
||||
@@ -162,6 +295,9 @@ void vkb_cbtrack_device_init(vkb_srv_table *t)
|
||||
vkb_cbtrack *k = calloc(1, sizeof(*k));
|
||||
if (!k) return;
|
||||
pthread_mutex_init(&k->lock, NULL);
|
||||
k->create_pool = t->dt.vkCreateCommandPool;
|
||||
k->reset_pool = t->dt.vkResetCommandPool;
|
||||
k->reset_cb = t->dt.vkResetCommandBuffer;
|
||||
k->alloc = t->dt.vkAllocateCommandBuffers;
|
||||
k->free_cbs = t->dt.vkFreeCommandBuffers;
|
||||
k->destroy_pool = t->dt.vkDestroyCommandPool;
|
||||
@@ -175,6 +311,9 @@ void vkb_cbtrack_device_init(vkb_srv_table *t)
|
||||
t->dt.vkBeginCommandBuffer = cbt_BeginCommandBuffer;
|
||||
t->dt.vkQueueSubmit = cbt_QueueSubmit;
|
||||
if (k->submit2) t->dt.vkQueueSubmit2 = cbt_QueueSubmit2;
|
||||
if (k->create_pool) t->dt.vkCreateCommandPool = cbt_CreateCommandPool;
|
||||
if (k->reset_pool) t->dt.vkResetCommandPool = cbt_ResetCommandPool;
|
||||
if (k->reset_cb) t->dt.vkResetCommandBuffer = cbt_ResetCommandBuffer;
|
||||
}
|
||||
|
||||
void vkb_cbtrack_device_destroy(vkb_srv_table *t)
|
||||
@@ -188,6 +327,11 @@ void vkb_cbtrack_device_destroy(vkb_srv_table *t)
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
while (k->pools) {
|
||||
cbt_pool *p = k->pools;
|
||||
k->pools = p->next;
|
||||
free(p);
|
||||
}
|
||||
pthread_mutex_destroy(&k->lock);
|
||||
free(k);
|
||||
t->cbtrack = NULL;
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "vkb_server.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -211,6 +214,46 @@ static void order_done(vkb_srv_proc *p, uint64_t seq)
|
||||
|
||||
/* ------------------------------------------------------------------ command buffer replay */
|
||||
|
||||
#define REPLAY_RING 32
|
||||
|
||||
typedef struct replay_rec {
|
||||
const char *name;
|
||||
uint32_t len;
|
||||
uint32_t nw;
|
||||
uint64_t w[4];
|
||||
} replay_rec;
|
||||
|
||||
static __thread replay_rec t_replay[REPLAY_RING];
|
||||
static __thread uint32_t t_replay_n;
|
||||
static __thread VkCommandBuffer t_replay_cb;
|
||||
|
||||
static void replay_note(const char *name, const uint8_t *payload, uint32_t len)
|
||||
{
|
||||
replay_rec *r = &t_replay[t_replay_n++ % REPLAY_RING];
|
||||
r->name = name;
|
||||
r->len = len;
|
||||
r->nw = len / 8 < 4 ? len / 8 : 4;
|
||||
for (uint32_t i = 0; i < r->nw; i++) memcpy(&r->w[i], payload + i * 8, 8);
|
||||
}
|
||||
|
||||
static void replay_dump(vkb_srv_table *t)
|
||||
{
|
||||
char desc[320];
|
||||
vkb_cbtrack_describe(t, t_replay_cb, desc, sizeof(desc));
|
||||
VKB_ERR("crash detail: %s", desc);
|
||||
uint32_t first = t_replay_n > REPLAY_RING ? t_replay_n - REPLAY_RING : 0;
|
||||
VKB_ERR("crash detail: %u commands replayed, the last %u (oldest first; payload words after the command id):", t_replay_n,
|
||||
t_replay_n - first);
|
||||
for (uint32_t i = first; i < t_replay_n; i++) {
|
||||
const replay_rec *r = &t_replay[i % REPLAY_RING];
|
||||
char words[96] = "";
|
||||
size_t o = 0;
|
||||
for (uint32_t j = 0; j < r->nw && o < sizeof(words); j++)
|
||||
o += (size_t)snprintf(words + o, sizeof(words) - o, " %llx", (unsigned long long)r->w[j]);
|
||||
VKB_ERR("crash detail: #%u %s (%u bytes)%s", i, r->name, r->len, words);
|
||||
}
|
||||
}
|
||||
|
||||
static int is_stream_cmd(uint32_t id)
|
||||
{
|
||||
const char *n = vkb_cmd_names[id];
|
||||
@@ -234,6 +277,8 @@ void vkb_sv_vkEndCommandBuffer(vkb_srv_call *c)
|
||||
}
|
||||
VkResult begin_r = VK_SUCCESS;
|
||||
size_t pos = 0;
|
||||
t_replay_cb = cb;
|
||||
t_replay_n = 0;
|
||||
vkb_enc scratch;
|
||||
vkb_enc_init(&scratch);
|
||||
uint32_t ncmds = 0;
|
||||
@@ -258,6 +303,7 @@ void vkb_sv_vkEndCommandBuffer(vkb_srv_call *c)
|
||||
sub.conn = c->conn;
|
||||
sub.in_stream = 1;
|
||||
vkb_srv_current_cmd = vkb_cmd_names[id];
|
||||
replay_note(vkb_cmd_names[id], s + pos, n);
|
||||
vkb_srv_handlers[id](&sub);
|
||||
vkb_srv_current_cmd = vkb_cmd_names[VKB_CMD_vkEndCommandBuffer];
|
||||
scratch = sub.r;
|
||||
@@ -269,7 +315,12 @@ void vkb_sv_vkEndCommandBuffer(vkb_srv_call *c)
|
||||
ncmds++;
|
||||
}
|
||||
vkb_enc_free(&scratch);
|
||||
replay_note("vkEndCommandBuffer (the driver's)", NULL, 0);
|
||||
vkb_srv_current_cmd = "vkEndCommandBuffer (the driver's)";
|
||||
VkResult r = c->dt->vkEndCommandBuffer(cb);
|
||||
vkb_srv_current_cmd = vkb_cmd_names[VKB_CMD_vkEndCommandBuffer];
|
||||
t_replay_cb = VK_NULL_HANDLE;
|
||||
if (r == VK_SUCCESS) vkb_cbtrack_ended(c->table, cb);
|
||||
if (begin_r != VK_SUCCESS) r = begin_r;
|
||||
if (vkb_verbose) VKB_DBG("command buffer %p: %u commands, %zu bytes -> %d", (void *)cb, ncmds, len, r);
|
||||
vkb_enc_u32(&c->r, (uint32_t)r);
|
||||
@@ -449,6 +500,8 @@ static int srv_recv(vkb_srv_conn *conn, vkb_msg_hdr *h, uint8_t **payload, int *
|
||||
*/
|
||||
static __thread sigjmp_buf *t_guard;
|
||||
static __thread volatile sig_atomic_t t_fault;
|
||||
static __thread void *t_fault_addr;
|
||||
static __thread uintptr_t t_fault_pc;
|
||||
static struct sigaction prev_segv, prev_bus;
|
||||
static pthread_once_t guard_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
@@ -458,6 +511,14 @@ static void guard_handler(int sig, siginfo_t *si, void *uc)
|
||||
if (g) {
|
||||
t_guard = NULL;
|
||||
t_fault = sig;
|
||||
t_fault_addr = si ? si->si_addr : NULL;
|
||||
#if defined(__aarch64__)
|
||||
t_fault_pc = uc ? (uintptr_t)((ucontext_t *)uc)->uc_mcontext.pc : 0;
|
||||
#elif defined(__x86_64__)
|
||||
t_fault_pc = uc ? (uintptr_t)((ucontext_t *)uc)->uc_mcontext.gregs[REG_RIP] : 0;
|
||||
#else
|
||||
t_fault_pc = 0;
|
||||
#endif
|
||||
siglongjmp(*g, 1);
|
||||
}
|
||||
struct sigaction *p = sig == SIGBUS ? &prev_bus : &prev_segv;
|
||||
@@ -532,6 +593,18 @@ static void *conn_main(void *arg)
|
||||
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);
|
||||
{
|
||||
Dl_info di;
|
||||
memset(&di, 0, sizeof(di));
|
||||
int found = t_fault_pc && dladdr((void *)t_fault_pc, &di);
|
||||
VKB_ERR("crash detail: inside %s, fault address %p, pc %p = %s+0x%lx (%s)",
|
||||
vkb_srv_current_cmd ? vkb_srv_current_cmd : "?", t_fault_addr, (void *)t_fault_pc,
|
||||
found && di.dli_fname ? di.dli_fname : "?",
|
||||
found && di.dli_fbase ? (unsigned long)(t_fault_pc - (uintptr_t)di.dli_fbase) : 0ul,
|
||||
found && di.dli_sname ? di.dli_sname : "no symbol");
|
||||
}
|
||||
if (h.cmd == VKB_CMD_vkEndCommandBuffer && t_replay_cb) replay_dump(c.table);
|
||||
t_replay_cb = VK_NULL_HANDLE;
|
||||
if (conn->proc) conn->proc->driver_crashed = 1;
|
||||
vkb_srv_current_cmd = NULL;
|
||||
vkb_srv_set_current_table(NULL);
|
||||
|
||||
@@ -103,6 +103,8 @@ void vkb_log_summary(void);
|
||||
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);
|
||||
void vkb_cbtrack_describe(vkb_srv_table *t, VkCommandBuffer cb, char *out, size_t n);
|
||||
void vkb_cbtrack_ended(vkb_srv_table *t, VkCommandBuffer cb);
|
||||
|
||||
/* server_spvfix.c */
|
||||
void vkb_spvfix_device_init(vkb_srv_table *t);
|
||||
|
||||
@@ -152,6 +152,16 @@ static void on_client_destroyed(struct wl_listener *l, void *data) {
|
||||
free(ci);
|
||||
}
|
||||
|
||||
/* libwayland's own reasons for dropping a client (a failed read or flush, an invalid object, a
|
||||
* malformed request) go through wl_log, which on Android writes to a closed stderr. */
|
||||
static void wl_log_to_session(const char *fmt, va_list ap) {
|
||||
char line[512];
|
||||
vsnprintf(line, sizeof(line), fmt, ap);
|
||||
size_t n = strlen(line);
|
||||
while (n && (line[n - 1] == '\n' || line[n - 1] == '\r')) line[--n] = 0;
|
||||
droiddeck_log("program", "libwayland: %s", line);
|
||||
}
|
||||
|
||||
static void on_client_created(struct wl_listener *l, void *data) {
|
||||
struct wl_client *client = data;
|
||||
struct client_info *ci = calloc(1, sizeof(*ci));
|
||||
@@ -3254,6 +3264,10 @@ int droiddeck_wayland_run(void) {
|
||||
return 1;
|
||||
}
|
||||
droiddeck_log("display", "Wayland compositor listening on %s/wayland-0", rt ? rt : "?");
|
||||
wl_log_set_handler_server(wl_log_to_session);
|
||||
/* A client that falls behind reading (gamescope at a few fps) must not be dropped for a full
|
||||
* event buffer: libwayland's default is 4 KB per direction. */
|
||||
wl_display_set_default_max_buffer_size(display, 1u << 20);
|
||||
wl_display_add_client_created_listener(display, &g_client_created);
|
||||
|
||||
wl_global_create(display, &wl_compositor_interface, 6, NULL, bind_compositor);
|
||||
|
||||
@@ -140,6 +140,7 @@ class MainActivity : ComponentActivity() {
|
||||
private var zinkLazy by mutableStateOf(false)
|
||||
private var vkBridge by mutableStateOf(false)
|
||||
private var venus by mutableStateOf(false)
|
||||
private var venusHostVulkan by mutableStateOf<String?>(null)
|
||||
private val adrenoGpu by lazy { com.droiddeck.launcher.core.DeviceSupport.adreno() }
|
||||
private var noXalia by mutableStateOf(true)
|
||||
private var fastSync by mutableStateOf(false)
|
||||
@@ -359,6 +360,7 @@ class MainActivity : ComponentActivity() {
|
||||
private var graphicsLogs by mutableStateOf(false)
|
||||
private var steamGpuDrawing by mutableStateOf(false)
|
||||
private var bridgeFellBack by mutableStateOf(false)
|
||||
private var bridgeFellBackMemory by mutableStateOf(false)
|
||||
private var showRoms by mutableStateOf(false)
|
||||
private var homeAppSelected by mutableStateOf(false)
|
||||
private var homeScreenEnabled by mutableStateOf(false)
|
||||
@@ -543,14 +545,14 @@ class MainActivity : ComponentActivity() {
|
||||
FrontEndScreen(
|
||||
FrontEndState(
|
||||
installed = installed, ready = ready, available = available?.version, removalPending = removalPending, runtimeActionsBlocked = runtimeChangesBlocked(),
|
||||
vkBridge = vkBridge, venus = venus, adreno = adrenoGpu,
|
||||
vkBridge = vkBridge, venus = venus, venusHostVulkan = venusHostVulkan, adreno = adrenoGpu,
|
||||
shortcutPicker = shortcutPicker,
|
||||
shortcutLibraryScanning = shortcutLibraryScanning,
|
||||
gameSyncFolder = gameSyncFolder,
|
||||
busy = busy, stage = stage, percent = percent,
|
||||
desktopInstalled = desktopInstalled, desktopPresent = desktopPresent,
|
||||
offlineAccount = offlineAccount, offline = offline,
|
||||
frameGenLabel = frameGenLabel, romsDir = romsDir, logsEnabled = logsEnabled, agentCommands = agentCommands, graphicsLogs = graphicsLogs, steamGpuDrawing = steamGpuDrawing, bridgeFellBack = bridgeFellBack,
|
||||
frameGenLabel = frameGenLabel, romsDir = romsDir, logsEnabled = logsEnabled, agentCommands = agentCommands, graphicsLogs = graphicsLogs, steamGpuDrawing = steamGpuDrawing, bridgeFellBack = bridgeFellBack, bridgeFellBackMemory = bridgeFellBackMemory,
|
||||
steamGames = steamGames, emulators = emulatorList, running = runningLabel,
|
||||
frameGen = FrameGen.mode(this),
|
||||
lossless = lossless,
|
||||
@@ -671,7 +673,14 @@ class MainActivity : ComponentActivity() {
|
||||
onRemovePackage = { id -> removePackage(id) },
|
||||
onRuntime = { onRuntimeButton() },
|
||||
onVkBridge = { on -> SessionPrefs.setVkBridge(this, on); vkBridge = on },
|
||||
onVenus = { on -> SessionPrefs.setVenus(this, on); venus = on },
|
||||
onVenus = { on ->
|
||||
SessionPrefs.setVenus(this, on)
|
||||
venus = on
|
||||
if (on && !com.droiddeck.launcher.core.DeviceSupport.adreno()) Thread({
|
||||
com.droiddeck.launcher.gpu.VulkanInfo.query()["api"]?.let { SessionPrefs.setHostVulkanApi(this, it) }
|
||||
runOnUiThread { venusHostVulkan = com.droiddeck.launcher.session.VenusComponent.hostVulkanTooOld(this) }
|
||||
}, "venus-host-vulkan").start()
|
||||
},
|
||||
onFrameGenPick = { mode ->
|
||||
FrameGen.set(this, mode)
|
||||
frameGenLabel = FrameGen.label(this)
|
||||
@@ -1564,6 +1573,7 @@ class MainActivity : ComponentActivity() {
|
||||
zinkLazy = SessionPrefs.zinkLazy(this)
|
||||
vkBridge = SessionPrefs.vkBridge(this)
|
||||
venus = SessionPrefs.venus(this)
|
||||
venusHostVulkan = com.droiddeck.launcher.session.VenusComponent.hostVulkanTooOld(this)
|
||||
noXalia = SessionPrefs.noXalia(this)
|
||||
fastSync = SessionPrefs.fastSync(this)
|
||||
syncFallback = SessionPrefs.syncFallback(this)
|
||||
@@ -1593,6 +1603,7 @@ class MainActivity : ComponentActivity() {
|
||||
graphicsLogs = SessionPrefs.detailedGraphicsLogs(this)
|
||||
steamGpuDrawing = SessionPrefs.steamGpuCompositing(this)
|
||||
bridgeFellBack = SessionPrefs.bridgeFellBack(this)
|
||||
bridgeFellBackMemory = SessionPrefs.bridgeFellBackForMemory(this)
|
||||
runningLabel = if (SessionState.running) when (SessionState.mode) {
|
||||
SessionService.MODE_DESKTOP -> getString(R.string.rail_desktop)
|
||||
SessionService.MODE_RUN -> Library.nameForProgram(SessionState.program)
|
||||
|
||||
@@ -15,6 +15,8 @@ object DriverPairs {
|
||||
/** The open non-Adreno drivers (The412Banner/droiddeck-nonadreno-drivers), one pair per driver. */
|
||||
const val NON_ADRENO = "nad"
|
||||
const val NAD_RADV_XCLIPSE = "nad:radv-xclipse"
|
||||
/** RADV-Xclipse rebased on JimVulkan's upstream: a manual choice, never Auto. */
|
||||
const val NAD_RADV_XCLIPSE_UPSTREAM = "nad:radv-xclipse-upstream"
|
||||
const val NAD_MALI_JM = "nad:panvk-mali-jm"
|
||||
const val NAD_MALI_G710 = "nad:panvk-mali-g710"
|
||||
const val NAD_MALI_G615 = "nad:panvk-mali-g615"
|
||||
@@ -38,6 +40,7 @@ object DriverPairs {
|
||||
|
||||
private fun describeNonAdreno(key: String): Pair<String, Set<Family>> = when (key) {
|
||||
NAD_RADV_XCLIPSE -> "RADV · Samsung Xclipse" to setOf(Family.POWERVR)
|
||||
NAD_RADV_XCLIPSE_UPSTREAM -> "RADV upstream · Samsung Xclipse" to setOf(Family.POWERVR)
|
||||
NAD_MALI_JM -> "PanVK · Mali G52-G78" to setOf(Family.MALI_JM)
|
||||
NAD_MALI_G710 -> "PanVK · Mali-G610/G710" to setOf(Family.MALI_G610_G710)
|
||||
NAD_MALI_G615 -> "PanVK · Mali-G615" to setOf(Family.MALI_G615)
|
||||
@@ -64,7 +67,8 @@ object DriverPairs {
|
||||
get() = listOfNotNull(bundle?.tag, display?.tag, linux?.tag).distinct().joinToString(" + ") { it.removePrefix("linux-").removePrefix("DD-Turnip-") }
|
||||
fun suits(gpu: GpuInfo) = when {
|
||||
// The system-driver family covers PowerVR and Xclipse; RADV is only for the Xclipse.
|
||||
key == NAD_RADV_XCLIPSE -> gpu.family == Family.POWERVR && gpu.name.contains("Xclipse", ignoreCase = true)
|
||||
key == NAD_RADV_XCLIPSE || key == NAD_RADV_XCLIPSE_UPSTREAM ->
|
||||
gpu.family == Family.POWERVR && gpu.name.contains("Xclipse", ignoreCase = true)
|
||||
// One binary for Mali v9-v13, not the older G52/G76 in the Job Manager family.
|
||||
key == NAD_MALI_KBASE -> gpu.family.mali && GpuInfo.maliKbaseCovered(gpu.name)
|
||||
key in DEVICE_BUILDS -> gpu.family.mali && GpuInfo.maliNumber(gpu.name) == DEVICE_BUILDS[key]
|
||||
|
||||
@@ -57,7 +57,7 @@ object TurnipReleases {
|
||||
// Xclipse), both halves in one zip. One release per driver, replaced in place on each
|
||||
// rebuild; every one is a test build, published as a pre-release.
|
||||
Source("Non-Adreno", NON_ADRENO_REPO, prerelease = true) { name, _ ->
|
||||
val m = Regex("""^(RADV-Xclipse|PanVK-[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*?)-Pair\.zip$""").find(name) ?: return@Source null
|
||||
val m = Regex("""^(RADV-Xclipse(?:-[A-Za-z0-9]+)*?|PanVK-[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*?)-Pair\.zip$""").find(name) ?: return@Source null
|
||||
val key = DriverPairs.nonAdreno(m.groupValues[1])
|
||||
Kind(false, "Android + Linux · " + DriverPairs.nonAdrenoName(key), key, bundle = true)
|
||||
},
|
||||
|
||||
@@ -181,13 +181,23 @@ object GraphicsReport {
|
||||
val guestLines = File(dir, "session.log").takeIf { it.isFile }?.useLines { seq ->
|
||||
seq.filter { GUEST.containsMatchIn(it) }.take(200).toList()
|
||||
}.orEmpty()
|
||||
val fellBack = File(dir, "vkbridge-fallback").takeIf { it.isFile }?.readText()?.trim()
|
||||
runCatching { SessionPrefs.setBridgeFellBack(appContext ?: return@runCatching, fellBack != null) }
|
||||
val fallbackLines = File(dir, "vkbridge-fallback").takeIf { it.isFile }?.readLines()?.map { it.trim() }?.filter { it.isNotEmpty() }
|
||||
val fellBack = fallbackLines?.firstOrNull()
|
||||
val fallbackReason = fallbackLines?.getOrNull(1).orEmpty()
|
||||
runCatching { SessionPrefs.setBridgeFellBack(appContext ?: return@runCatching, fellBack != null, fallbackReason) }
|
||||
val limits = File(dir, "limits.txt").takeIf { it.isFile }?.readText()?.trim()
|
||||
val audio = File(dir, "audio.log").takeIf { it.isFile }?.readText().orEmpty()
|
||||
report.appendText(buildString {
|
||||
limits?.let { appendLine("open files limit: $it") }
|
||||
fellBack?.let { appendLine("!! MALI BRIDGE FELL BACK: the self-test failed images or draws; the session ran on $it") }
|
||||
fellBack?.let {
|
||||
if (fallbackReason == "memory") appendLine("!! MALI BRIDGE FELL BACK: memory sharing = none (no method passed the server's self-test); the session ran on $it")
|
||||
else appendLine("!! MALI BRIDGE FELL BACK: the self-test failed images or draws; the session ran on $it")
|
||||
}
|
||||
appContext?.let { ctx ->
|
||||
if (SessionPrefs.venus(ctx)) VenusComponent.hostVulkanTooOld(ctx)?.let {
|
||||
appendLine("!! VENUS OFF: the phone's driver reports Vulkan $it; gamescope needs 1.2 or newer, so the session did not use Venus")
|
||||
}
|
||||
}
|
||||
if (audio.contains("AAUDIO_ERROR_DISCONNECTED")) {
|
||||
appendLine("!! SOUND: Android refused the audio output (AAUDIO_ERROR_DISCONNECTED) - rebooting the phone usually fixes this")
|
||||
}
|
||||
|
||||
@@ -188,7 +188,8 @@ object SessionArtifacts {
|
||||
"audio.log, device.txt) were on disk already and are as they were left.\n" +
|
||||
(if (newest) "" else "Steam's logs are not included: a later session has overwritten them.\n") +
|
||||
"crash.log holds Android's crash buffer as of the next app start - if this\n" +
|
||||
"session died of a crash, the entry is in there unless the device rebooted.\n"
|
||||
"session died of a crash, the entry is in there unless the device rebooted.\n" +
|
||||
(if (newest) exitReasons(context) else "")
|
||||
)
|
||||
val record = Record(dir)
|
||||
if (newest) copySteamLogs(context, dir, record)
|
||||
@@ -203,6 +204,39 @@ object SessionArtifacts {
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitReasons(context: Context): String {
|
||||
if (android.os.Build.VERSION.SDK_INT < 30) return ""
|
||||
return runCatching {
|
||||
val am = context.getSystemService(android.app.ActivityManager::class.java) ?: return ""
|
||||
val reasons = am.getHistoricalProcessExitReasons(context.packageName, 0, 3)
|
||||
if (reasons.isEmpty()) return ""
|
||||
val fmt = java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss", java.util.Locale.US)
|
||||
buildString {
|
||||
append("\nAndroid's record of how this app's recent processes ended (newest first):\n")
|
||||
reasons.forEach { r ->
|
||||
append(" ${fmt.format(java.util.Date(r.timestamp))} ${r.processName} pid ${r.pid}: reason ${reasonName(r.reason)}, " +
|
||||
"status ${r.status}, importance ${r.importance}, pss ${r.pss / 1024} MB, rss ${r.rss / 1024} MB" +
|
||||
(r.description?.let { " - $it" } ?: "") + "\n")
|
||||
}
|
||||
}
|
||||
}.getOrDefault("")
|
||||
}
|
||||
|
||||
private fun reasonName(reason: Int): String = when (reason) {
|
||||
android.app.ApplicationExitInfo.REASON_LOW_MEMORY -> "LOW_MEMORY (killed for memory)"
|
||||
android.app.ApplicationExitInfo.REASON_SIGNALED -> "SIGNALED"
|
||||
android.app.ApplicationExitInfo.REASON_CRASH -> "CRASH"
|
||||
android.app.ApplicationExitInfo.REASON_CRASH_NATIVE -> "CRASH_NATIVE"
|
||||
android.app.ApplicationExitInfo.REASON_ANR -> "ANR"
|
||||
android.app.ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> "EXCESSIVE_RESOURCE_USAGE"
|
||||
android.app.ApplicationExitInfo.REASON_USER_REQUESTED -> "USER_REQUESTED"
|
||||
android.app.ApplicationExitInfo.REASON_USER_STOPPED -> "USER_STOPPED"
|
||||
android.app.ApplicationExitInfo.REASON_EXIT_SELF -> "EXIT_SELF"
|
||||
android.app.ApplicationExitInfo.REASON_DEPENDENCY_DIED -> "DEPENDENCY_DIED"
|
||||
android.app.ApplicationExitInfo.REASON_OTHER -> "OTHER"
|
||||
else -> reason.toString()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun prune(context: Context) {
|
||||
val current = SessionPaths.current()
|
||||
|
||||
@@ -441,11 +441,19 @@ object SessionPrefs {
|
||||
*/
|
||||
fun venus(context: Context): Boolean = prefs(context).getBoolean("venus", false)
|
||||
|
||||
fun hostVulkanApi(context: Context): String = prefs(context).getString("hostVulkanApi", "").orEmpty()
|
||||
|
||||
fun setHostVulkanApi(context: Context, api: String) {
|
||||
if (api != hostVulkanApi(context)) prefs(context).edit().putString("hostVulkanApi", api).apply()
|
||||
}
|
||||
|
||||
/** The last session's Mali bridge self-test failed and it ran on the pair instead (Setup says so). */
|
||||
fun bridgeFellBack(context: Context): Boolean = prefs(context).getBoolean("bridgeFellBack", false)
|
||||
|
||||
fun setBridgeFellBack(context: Context, on: Boolean) {
|
||||
prefs(context).edit().putBoolean("bridgeFellBack", on).apply()
|
||||
fun bridgeFellBackForMemory(context: Context): Boolean = prefs(context).getString("bridgeFallbackReason", "") == "memory"
|
||||
|
||||
fun setBridgeFellBack(context: Context, on: Boolean, reason: String = "") {
|
||||
prefs(context).edit().putBoolean("bridgeFellBack", on).putString("bridgeFallbackReason", if (on) reason else "").apply()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -297,6 +297,12 @@ class SessionService : Service() {
|
||||
|
||||
private fun runSession(gen: Int) {
|
||||
SessionPrefs.ensurePowerVrAutoDrivers(this)
|
||||
if (!com.droiddeck.launcher.core.DeviceSupport.adreno()) {
|
||||
com.droiddeck.launcher.gpu.VulkanInfo.query()["api"]?.let { SessionPrefs.setHostVulkanApi(this, it) }
|
||||
if (SessionPrefs.venus(this)) VenusComponent.hostVulkanTooOld(this)?.let {
|
||||
Log.w(TAG, "venus: off for this session - the phone's driver reports Vulkan $it and gamescope needs 1.2 or newer; the session uses the PanVK pair or the bridge")
|
||||
}
|
||||
}
|
||||
SessionTerminal.clear()
|
||||
try {
|
||||
LinuxRuntime.writeAccounts(this)
|
||||
|
||||
@@ -67,7 +67,16 @@ class VenusComponent(private val logFile: File?) : SessionPart() {
|
||||
@JvmStatic
|
||||
fun wanted(context: Context): Boolean =
|
||||
!DeviceSupport.adreno() && !com.droiddeck.launcher.gpu.SystemVulkanDriver.isAvailable() &&
|
||||
SessionPrefs.venus(context) && available(context)
|
||||
SessionPrefs.venus(context) && available(context) && hostVulkanTooOld(context) == null
|
||||
|
||||
/** The phone driver's Vulkan version when it is below 1.2: gamescope skips such a device, so Venus cannot run. */
|
||||
@JvmStatic
|
||||
fun hostVulkanTooOld(context: Context): String? {
|
||||
val api = com.droiddeck.launcher.gpu.VulkanInfo.cachedOrNull()?.get("api") ?: SessionPrefs.hostVulkanApi(context)
|
||||
val parts = api.split('.').mapNotNull { it.trim().toIntOrNull() }
|
||||
if (parts.size < 2) return null
|
||||
return api.takeIf { parts[0] < 1 || (parts[0] == 1 && parts[1] < 2) }
|
||||
}
|
||||
|
||||
/** Whether this apk carries the Venus component (tools/venus, venus.tzst). */
|
||||
@JvmStatic
|
||||
|
||||
@@ -101,6 +101,7 @@ class FrontEndState(
|
||||
val graphicsLogs: Boolean = false,
|
||||
val steamGpuDrawing: Boolean = false,
|
||||
val bridgeFellBack: Boolean = false,
|
||||
val bridgeFellBackMemory: Boolean = false,
|
||||
val steamGames: List<Library.SteamGame>,
|
||||
val emulators: List<Library.Emulator>,
|
||||
val running: String?,
|
||||
@@ -137,6 +138,7 @@ class FrontEndState(
|
||||
val vkBridge: Boolean = false,
|
||||
/** Setup's "Mali (Venus)" (SessionPrefs.venus). */
|
||||
val venus: Boolean = false,
|
||||
val venusHostVulkan: String? = null,
|
||||
val showPhantomGate: Boolean = false,
|
||||
val orientation: Int = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
|
||||
val launcherFullscreen: Boolean = true,
|
||||
|
||||
@@ -214,11 +214,13 @@ internal fun SetupPanel(
|
||||
when (gpu.support) {
|
||||
com.droiddeck.launcher.gpu.GpuInfo.Support.TESTED -> stringResource(R.string.setup_gpu_detail, gpu.displayName(ctx), gpuName)
|
||||
com.droiddeck.launcher.gpu.GpuInfo.Support.UNTESTED ->
|
||||
if (!s.adreno && s.vkBridge && !com.droiddeck.launcher.core.DeviceSupport.bridgeUsable()) stringResource(R.string.setup_gpu_bridge_unusable, gpu.displayName(ctx))
|
||||
else if (!s.adreno && s.vkBridge && s.bridgeFellBack) stringResource(R.string.setup_gpu_bridge_fell_back, gpu.displayName(ctx))
|
||||
if (!s.adreno && s.venus && s.venusHostVulkan != null) stringResource(R.string.setup_gpu_venus_host_too_old, gpu.displayName(ctx), s.venusHostVulkan)
|
||||
else if (!s.adreno && s.vkBridge && !com.droiddeck.launcher.core.DeviceSupport.bridgeUsable()) stringResource(R.string.setup_gpu_bridge_unusable, gpu.displayName(ctx))
|
||||
else if (!s.adreno && s.vkBridge && s.bridgeFellBack) stringResource(if (s.bridgeFellBackMemory) R.string.setup_gpu_bridge_no_memory_sharing else R.string.setup_gpu_bridge_fell_back, gpu.displayName(ctx))
|
||||
else stringResource(R.string.setup_gpu_untested_detail, gpu.displayName(ctx), gpu.supportText(ctx).replaceFirstChar { it.lowercase() })
|
||||
else -> if (s.vkBridge && !com.droiddeck.launcher.core.DeviceSupport.bridgeUsable()) stringResource(R.string.setup_gpu_bridge_unusable, gpuName)
|
||||
else if (s.vkBridge && s.bridgeFellBack) stringResource(R.string.setup_gpu_bridge_fell_back, gpuName)
|
||||
else -> if (s.venus && s.venusHostVulkan != null) stringResource(R.string.setup_gpu_venus_host_too_old, gpuName, s.venusHostVulkan)
|
||||
else if (s.vkBridge && !com.droiddeck.launcher.core.DeviceSupport.bridgeUsable()) stringResource(R.string.setup_gpu_bridge_unusable, gpuName)
|
||||
else if (s.vkBridge && s.bridgeFellBack) stringResource(if (s.bridgeFellBackMemory) R.string.setup_gpu_bridge_no_memory_sharing else R.string.setup_gpu_bridge_fell_back, gpuName)
|
||||
else if (s.vkBridge) stringResource(R.string.setup_gpu_bridge_detail, gpuName)
|
||||
else stringResource(R.string.setup_gpu_unsupported_detail, gpuName)
|
||||
},
|
||||
|
||||
@@ -721,6 +721,7 @@
|
||||
<string name="setup_steam_gpu_drawing">Steam interface: GPU drawing</string>
|
||||
<string name="setup_steam_gpu_drawing_hint">With the Xclipse driver pair, Steam draws its pages in software (GPU drawing showed broken tiles). Turn on for a faster Steam interface if it looks right on this phone.</string>
|
||||
<string name="setup_gpu_bridge_fell_back">The Mali bridge failed its self-test on %1$s last session (images or draws), so the session used the PanVK driver pair instead.</string>
|
||||
<string name="setup_gpu_bridge_no_memory_sharing">The Mali bridge found no way to share memory with the driver on %1$s last session, so the session used the PanVK driver pair instead. The bridge cannot work on this phone; turning it off keeps the PanVK pair.</string>
|
||||
<string name="setup_graphics_logs_hint">Each game\'s Proton, DXVK and driver logs in the session\'s logs. Always on when the GPU is not an Adreno.</string>
|
||||
<string name="setup_latest_logs">Latest session logs</string>
|
||||
<string name="setup_saved_logs">Saved session logs</string>
|
||||
@@ -2237,6 +2238,7 @@
|
||||
<string name="achievement_unlocked">Achievement unlocked</string>
|
||||
<string name="setup_gpu_venus_off">Mali (Venus): off</string>
|
||||
<string name="setup_gpu_venus_on">Mali (Venus): on</string>
|
||||
<string name="setup_gpu_venus_host_too_old">The driver on %1$s reports Vulkan %2$s. Mali (Venus) needs Vulkan 1.2 or newer (gamescope does not start on older), so sessions skip Venus and use the PanVK driver pair instead.</string>
|
||||
<string name="setup_gpu_bridge_off">Mali bridge: off</string>
|
||||
<string name="setup_gpu_bridge_unusable">%1$s cannot use the Mali bridge (it needs a Mali GPU without a system-driver route), so this switch has no effect here.</string>
|
||||
<string name="setup_gpu_bridge_on">Mali bridge: on</string>
|
||||
|
||||
@@ -201,6 +201,22 @@ class DriverPairsTest {
|
||||
assertFalse(DriverPairs.isNonAdreno(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun upstreamRadvIsAManualXclipseChoiceAndAutoKeepsOurs() {
|
||||
val asset = { id: String -> TurnipReleases.Asset("Non-Adreno", id.lowercase(), "$id-Pair.zip", "https://example/$id", 9_000_000,
|
||||
false, "label", "0".repeat(64), DriverPairs.nonAdreno(id), bundle = true) }
|
||||
val pairs = DriverPairs.from(TurnipReleases.Check(listOf(asset("RADV-Xclipse"), asset("RADV-Xclipse-upstream")), emptyList(), emptyList(), 0L))
|
||||
val xclipse = GpuInfo("Samsung Xclipse 940", 0, Family.POWERVR, "", false)
|
||||
val upstream = pairs.single { it.key == DriverPairs.NAD_RADV_XCLIPSE_UPSTREAM }
|
||||
assertEquals("nad:radv-xclipse-upstream", DriverPairs.nonAdreno("RADV-Xclipse-upstream"))
|
||||
assertTrue(upstream.complete)
|
||||
assertTrue(upstream.suits(xclipse))
|
||||
assertFalse(upstream.suits(GpuInfo("PowerVR", 0, Family.POWERVR, "", false)))
|
||||
assertEquals("RADV upstream · Samsung Xclipse", upstream.name)
|
||||
assertEquals(DriverPairs.NAD_RADV_XCLIPSE, DriverPairs.recommendedKey(xclipse, pairs))
|
||||
assertTrue(DriverPairs.isNonAdreno("RADV-Xclipse-upstream"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deviceBuildsAreOfferedOnlyForTheirMaliAndNeverByAuto() {
|
||||
fun bundle(id: String) = TurnipReleases.Asset("Non-Adreno", id.lowercase(), "$id-Pair.zip", "https://example/$id", 9_000_000,
|
||||
|
||||
@@ -132,13 +132,26 @@ if [ "${BL_VKBRIDGE:-0}" = 1 ]; then
|
||||
step "vkbridge self-test"
|
||||
bl_vkb_smoke=$(timeout 60 /usr/local/bin/droiddeck-vkbridge-smoke 2>&1)
|
||||
printf '%s\n' "$bl_vkb_smoke" | sed 's/^/== vkbridge: /'
|
||||
if printf '%s\n' "$bl_vkb_smoke" | grep -qE '^FAIL (image clear|draw)' && [ -r "${BL_VKBRIDGE_FALLBACK_ICD:-}" ]; then
|
||||
bl_vkb_why=
|
||||
if printf '%s\n' "$bl_vkb_smoke" | grep -qE '^FAIL memory sharing'; then
|
||||
bl_vkb_why=memory
|
||||
elif printf '%s\n' "$bl_vkb_smoke" | grep -qE '^FAIL (image clear|draw)'; then
|
||||
bl_vkb_why=draw
|
||||
fi
|
||||
if [ -n "$bl_vkb_why" ] && [ -r "${BL_VKBRIDGE_FALLBACK_ICD:-}" ]; then
|
||||
export VK_DRIVER_FILES=$BL_VKBRIDGE_FALLBACK_ICD VK_ICD_FILENAMES=$BL_VKBRIDGE_FALLBACK_ICD BL_VKBRIDGE=0
|
||||
export BL_OPEN_DRIVER=1 BL_DRM_STANDIN=/dev/mali0
|
||||
[ "${BL_STEAM_GPU_COMPOSITING:-0}" = 1 ] || export BL_STEAM_SOFT_COMPOSITING=1
|
||||
echo "== vkbridge: self-test failed images or draws; this session uses the PanVK pair instead ($BL_VKBRIDGE_FALLBACK_ICD)"
|
||||
[ -n "${BL_DEBUG_DIR:-}" ] && echo "$BL_VKBRIDGE_FALLBACK_ICD" > "$BL_DEBUG_DIR/vkbridge-fallback" 2>/dev/null
|
||||
if [ "$bl_vkb_why" = memory ]; then
|
||||
echo "== vkbridge: memory sharing = none (no method passed the server's self-test); this session uses the PanVK pair instead ($BL_VKBRIDGE_FALLBACK_ICD)"
|
||||
else
|
||||
echo "== vkbridge: self-test failed images or draws; this session uses the PanVK pair instead ($BL_VKBRIDGE_FALLBACK_ICD)"
|
||||
fi
|
||||
[ -n "${BL_DEBUG_DIR:-}" ] && printf '%s\n%s\n' "$BL_VKBRIDGE_FALLBACK_ICD" "$bl_vkb_why" > "$BL_DEBUG_DIR/vkbridge-fallback" 2>/dev/null
|
||||
elif [ -n "$bl_vkb_why" ]; then
|
||||
echo "== vkbridge: self-test failed ($bl_vkb_why) and no PanVK pair is installed to fall back on; expect a black screen or a lost device - set a Mali driver pair in Setup"
|
||||
fi
|
||||
unset bl_vkb_why
|
||||
unset bl_vkb_smoke
|
||||
if command -v vulkaninfo >/dev/null 2>&1; then
|
||||
timeout 30 vulkaninfo --summary 2>&1 | sed -n '/^Devices:/,$p' | head -40 | sed 's/^/== vulkaninfo: /'
|
||||
@@ -172,6 +185,15 @@ if [ "${BL_GAME_LOGS:-0}" = 1 ] && [ -z "${BL_INSIDE:-}" ] && [ -n "${BL_DEBUG_D
|
||||
if command -v vulkaninfo >/dev/null 2>&1; then
|
||||
timeout 10 vulkaninfo --summary
|
||||
echo "== vulkaninfo exit: $?"
|
||||
if timeout 20 vulkaninfo > "$BL_DEBUG_DIR/vulkan-full.txt" 2>&1; then
|
||||
echo "== device extensions (full list in vulkan-full.txt):"
|
||||
sed -n '/^Device Extensions:/,/^$/p' "$BL_DEBUG_DIR/vulkan-full.txt" | grep -o 'VK_[A-Za-z0-9_]*' | sort -u | tr '\n' ' ' | fold -s -w 200
|
||||
echo
|
||||
echo "== key features:"
|
||||
grep -E '^[[:space:]]+(robustBufferAccess2?|robustImageAccess2?|nullDescriptor|logicOp|fillModeNonSolid|shaderClipDistance|shaderCullDistance|dualSrcBlend|geometryShader|scalarBlockLayout|timelineSemaphore|dynamicRendering|synchronization2|maintenance4|maintenance5|maintenance6|shaderDrawParameters|descriptorIndexing|bufferDeviceAddress|vulkanMemoryModel|hostQueryReset|imagelessFramebuffer|samplerYcbcrConversion|shaderInt64|shaderFloat64|shaderInt16|shaderFloat16|storageBuffer16BitAccess|extendedDynamicState[23]?|vertexInputDynamicState|customBorderColors|customBorderColorWithoutFormat|depthClipEnable|multiViewport|textureCompressionBC|indexTypeUint8|rectangularLines|bresenhamLines|transformFeedback|geometryStreams|graphicsPipelineLibrary|pushDescriptor|primitiveTopologyListRestart|dynamicRenderingLocalRead|shaderDemoteToHelperInvocation|subgroupSizeControl|computeFullSubgroups|inlineUniformBlock|pipelineCreationCacheControl) = ' "$BL_DEBUG_DIR/vulkan-full.txt" | sed 's/^[[:space:]]*/ /' | sort -u
|
||||
else
|
||||
echo "== full vulkaninfo failed or timed out (exit $?)"
|
||||
fi
|
||||
else
|
||||
echo "== vulkaninfo is not in this runtime; GPU nodes:"
|
||||
ls -l /dev/kgsl-3d0 /dev/mali0 /dev/dri 2>&1
|
||||
@@ -994,7 +1016,10 @@ BUSCONF
|
||||
LIBGL_DEBUG=verbose MESA_LOG_LEVEL=debug timeout 20 /usr/local/bin/droiddeck-glprobe 2>&1 | grep -v drirc | head -60 | sed 's/^/== glprobe: /'
|
||||
if [ "${PIPESTATUS[0]}" = 1 ]; then
|
||||
VK_LOADER_DEBUG=error,warn timeout 20 /usr/local/bin/droiddeck-glprobe 2>&1 | grep -v drirc | head -40 | sed 's/^/== glprobe (loader): /'
|
||||
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): /'
|
||||
DROIDDECK_MALI_COMPAT_TRACE=1 MESA_DEBUG=1 MESA_LOG_LEVEL=debug LIBGL_DEBUG=verbose ZINK_DEBUG=mem VN_DEBUG=init,result,log_ctx_info \
|
||||
timeout 30 /usr/local/bin/droiddeck-glprobe > "${BL_DEBUG_DIR:-/tmp}/glprobe-trace.txt" 2>&1
|
||||
grep -v drirc "${BL_DEBUG_DIR:-/tmp}/glprobe-trace.txt" | head -120 | sed 's/^/== glprobe (trace): /'
|
||||
echo "== glprobe (trace): last pre-device query before Zink gave up: $(grep 'droiddeck-mali-compat: trace:' "${BL_DEBUG_DIR:-/tmp}/glprobe-trace.txt" | grep -v 'trace: extensions:' | tail -1)"
|
||||
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
|
||||
|
||||
@@ -414,8 +414,10 @@ 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())
|
||||
if (tracing()) {
|
||||
layer_log("trace: vkEnumerateDeviceExtensionProperties: %u from the driver, %u added by the layer", own, extra_count);
|
||||
trace_extension_names(all, total);
|
||||
}
|
||||
uint32_t n = *count < total ? *count : total;
|
||||
memcpy(properties, all, n * sizeof(*all));
|
||||
free(all);
|
||||
@@ -470,6 +472,8 @@ static VKAPI_ATTR VkResult VKAPI_CALL CreateInstance(const VkInstanceCreateInfo
|
||||
LOAD_KHR(GetPhysicalDeviceImageFormatProperties2)
|
||||
#undef LOAD
|
||||
#undef LOAD_KHR
|
||||
if (tracing())
|
||||
trace_load(inst);
|
||||
/* Without the Vulkan 1.1 queries there is no way to answer the chained ones. */
|
||||
if (!inst->GetPhysicalDeviceFeatures2 || !inst->GetPhysicalDeviceProperties2 ||
|
||||
!inst->GetPhysicalDeviceFormatProperties2 || !inst->GetPhysicalDeviceImageFormatProperties2)
|
||||
@@ -781,6 +785,11 @@ static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetInstanceProcAddr(VkInstance i
|
||||
if (!strcmp(name, "vkCreateDevice"))
|
||||
return (PFN_vkVoidFunction)CreateDevice;
|
||||
PFN_vkVoidFunction next = inst->gipa(instance, name);
|
||||
if (next && tracing()) {
|
||||
PFN_vkVoidFunction traced = trace_hook(inst, name);
|
||||
if (traced)
|
||||
return traced;
|
||||
}
|
||||
if (!next || !inst->active)
|
||||
return next;
|
||||
for (size_t i = 0; i < sizeof(physical_device_hooks) / sizeof(physical_device_hooks[0]); i++) {
|
||||
|
||||
@@ -70,6 +70,13 @@ struct instance {
|
||||
PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
|
||||
PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties;
|
||||
PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2;
|
||||
PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices;
|
||||
PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties;
|
||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2;
|
||||
PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
|
||||
PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
|
||||
PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
|
||||
pthread_mutex_t lock;
|
||||
struct physical_device physical_devices[8];
|
||||
uint32_t physical_device_count;
|
||||
@@ -273,6 +280,11 @@ struct instance *instance_from_handle(const void *dispatchable);
|
||||
/* What the GPU lacks of the features the layer can make up for. */
|
||||
uint32_t physical_device_gaps(struct instance *inst, VkPhysicalDevice physical_device);
|
||||
|
||||
/* trace.c (DROIDDECK_MALI_COMPAT_TRACE=1) */
|
||||
void trace_load(struct instance *inst);
|
||||
PFN_vkVoidFunction trace_hook(struct instance *inst, const char *name);
|
||||
void trace_extension_names(const VkExtensionProperties *ext, uint32_t n);
|
||||
|
||||
|
||||
/* draw.c */
|
||||
VKAPI_ATTR void VKAPI_CALL CmdDrawIndirect(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, uint32_t draw_count, uint32_t stride);
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* DROIDDECK_MALI_COMPAT_TRACE=1: the physical-device queries an application makes before
|
||||
* vkCreateDevice, logged as they pass through. Zink on Venus over an older Mali driver gave up
|
||||
* after vkGetPhysicalDeviceProperties2 without a word (its release builds print nothing there),
|
||||
* so the last query in the log is where it stopped.
|
||||
*/
|
||||
#include "mali_compat.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint32_t format_queries;
|
||||
|
||||
static bool format_worth_logging(uint32_t n, VkFormatFeatureFlags linear, VkFormatFeatureFlags optimal, VkFormatFeatureFlags buffer)
|
||||
{
|
||||
return n == 1 || n % 25 == 0 || (!linear && !optimal && !buffer);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL TraceEnumeratePhysicalDevices(VkInstance instance, uint32_t *count, VkPhysicalDevice *devices)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(instance);
|
||||
VkResult r = inst->EnumeratePhysicalDevices(instance, count, devices);
|
||||
layer_log("trace: vkEnumeratePhysicalDevices (%s): %d, %u device(s)", devices ? "list" : "count", (int)r, count ? *count : 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceMemoryProperties(VkPhysicalDevice pd, VkPhysicalDeviceMemoryProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceMemoryProperties(pd, p);
|
||||
char types[384] = "";
|
||||
size_t o = 0;
|
||||
for (uint32_t i = 0; i < p->memoryTypeCount && o + 16 < sizeof(types); i++)
|
||||
o += (size_t)snprintf(types + o, sizeof(types) - o, " %u:h%u/0x%x", i, p->memoryTypes[i].heapIndex,
|
||||
(unsigned)p->memoryTypes[i].propertyFlags);
|
||||
layer_log("trace: vkGetPhysicalDeviceMemoryProperties: %u heaps, %u types:%s", p->memoryHeapCount, p->memoryTypeCount, types);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice pd, VkPhysicalDeviceMemoryProperties2 *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceMemoryProperties2(pd, p);
|
||||
char chain[160] = "";
|
||||
size_t o = 0;
|
||||
for (VkBaseOutStructure *s = p->pNext; s && o + 16 < sizeof(chain); s = s->pNext)
|
||||
o += (size_t)snprintf(chain + o, sizeof(chain) - o, " %d", (int)s->sType);
|
||||
layer_log("trace: vkGetPhysicalDeviceMemoryProperties2: %u heaps, %u types, chain:%s", p->memoryProperties.memoryHeapCount,
|
||||
p->memoryProperties.memoryTypeCount, chain);
|
||||
}
|
||||
|
||||
static void log_queue_families(const char *what, uint32_t n, const VkQueueFamilyProperties *(*at)(const void *, uint32_t), const void *arr)
|
||||
{
|
||||
char fams[256] = "";
|
||||
size_t o = 0;
|
||||
for (uint32_t i = 0; arr && i < n && o + 24 < sizeof(fams); i++) {
|
||||
const VkQueueFamilyProperties *q = at(arr, i);
|
||||
o += (size_t)snprintf(fams + o, sizeof(fams) - o, " %u:0x%x*%u", i, (unsigned)q->queueFlags, q->queueCount);
|
||||
}
|
||||
layer_log("trace: %s: %u famil%s%s", what, n, n == 1 ? "y" : "ies", fams);
|
||||
}
|
||||
|
||||
static const VkQueueFamilyProperties *qf1(const void *a, uint32_t i) { return &((const VkQueueFamilyProperties *)a)[i]; }
|
||||
static const VkQueueFamilyProperties *qf2(const void *a, uint32_t i) { return &((const VkQueueFamilyProperties2 *)a)[i].queueFamilyProperties; }
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice pd, uint32_t *n, VkQueueFamilyProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceQueueFamilyProperties(pd, n, p);
|
||||
log_queue_families("vkGetPhysicalDeviceQueueFamilyProperties", n ? *n : 0, qf1, p);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice pd, uint32_t *n, VkQueueFamilyProperties2 *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceQueueFamilyProperties2(pd, n, p);
|
||||
log_queue_families("vkGetPhysicalDeviceQueueFamilyProperties2", n ? *n : 0, qf2, p);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceFormatProperties(VkPhysicalDevice pd, VkFormat format, VkFormatProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
if (inst->active) GetPhysicalDeviceFormatProperties(pd, format, p);
|
||||
else inst->GetPhysicalDeviceFormatProperties(pd, format, p);
|
||||
uint32_t n = __atomic_add_fetch(&format_queries, 1, __ATOMIC_RELAXED);
|
||||
if (format_worth_logging(n, p->linearTilingFeatures, p->optimalTilingFeatures, p->bufferFeatures))
|
||||
layer_log("trace: vkGetPhysicalDeviceFormatProperties #%u: format %d linear 0x%x optimal 0x%x buffer 0x%x", n, (int)format,
|
||||
(unsigned)p->linearTilingFeatures, (unsigned)p->optimalTilingFeatures, (unsigned)p->bufferFeatures);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceFormatProperties2(VkPhysicalDevice pd, VkFormat format, VkFormatProperties2 *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
if (inst->active) GetPhysicalDeviceFormatProperties2(pd, format, p);
|
||||
else inst->GetPhysicalDeviceFormatProperties2(pd, format, p);
|
||||
uint32_t n = __atomic_add_fetch(&format_queries, 1, __ATOMIC_RELAXED);
|
||||
const VkFormatProperties *f = &p->formatProperties;
|
||||
if (format_worth_logging(n, f->linearTilingFeatures, f->optimalTilingFeatures, f->bufferFeatures))
|
||||
layer_log("trace: vkGetPhysicalDeviceFormatProperties2 #%u: format %d linear 0x%x optimal 0x%x buffer 0x%x", n, (int)format,
|
||||
(unsigned)f->linearTilingFeatures, (unsigned)f->optimalTilingFeatures, (unsigned)f->bufferFeatures);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL TraceGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice pd, VkFormat format, VkImageType type,
|
||||
VkImageTiling tiling, VkImageUsageFlags usage,
|
||||
VkImageCreateFlags flags, VkImageFormatProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
VkResult r = inst->active ? GetPhysicalDeviceImageFormatProperties(pd, format, type, tiling, usage, flags, p)
|
||||
: inst->GetPhysicalDeviceImageFormatProperties(pd, format, type, tiling, usage, flags, p);
|
||||
if (r != VK_SUCCESS)
|
||||
layer_log("trace: vkGetPhysicalDeviceImageFormatProperties: format %d type %d tiling %d usage 0x%x flags 0x%x -> %d", (int)format,
|
||||
(int)type, (int)tiling, (unsigned)usage, (unsigned)flags, (int)r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR VkResult VKAPI_CALL TraceGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice pd, const VkPhysicalDeviceImageFormatInfo2 *info,
|
||||
VkImageFormatProperties2 *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
VkResult r = inst->active ? GetPhysicalDeviceImageFormatProperties2(pd, info, p) : inst->GetPhysicalDeviceImageFormatProperties2(pd, info, p);
|
||||
if (r != VK_SUCCESS)
|
||||
layer_log("trace: vkGetPhysicalDeviceImageFormatProperties2: format %d type %d tiling %d usage 0x%x flags 0x%x -> %d", (int)info->format,
|
||||
(int)info->type, (int)info->tiling, (unsigned)info->usage, (unsigned)info->flags, (int)r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice pd, const VkPhysicalDeviceExternalBufferInfo *info,
|
||||
VkExternalBufferProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceExternalBufferProperties(pd, info, p);
|
||||
layer_log("trace: vkGetPhysicalDeviceExternalBufferProperties: handle 0x%x usage 0x%x -> features 0x%x", (unsigned)info->handleType,
|
||||
(unsigned)info->usage, (unsigned)p->externalMemoryProperties.externalMemoryFeatures);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice pd,
|
||||
const VkPhysicalDeviceExternalSemaphoreInfo *info,
|
||||
VkExternalSemaphoreProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceExternalSemaphoreProperties(pd, info, p);
|
||||
layer_log("trace: vkGetPhysicalDeviceExternalSemaphoreProperties: handle 0x%x -> features 0x%x", (unsigned)info->handleType,
|
||||
(unsigned)p->externalSemaphoreFeatures);
|
||||
}
|
||||
|
||||
static VKAPI_ATTR void VKAPI_CALL TraceGetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice pd, const VkPhysicalDeviceExternalFenceInfo *info,
|
||||
VkExternalFenceProperties *p)
|
||||
{
|
||||
struct instance *inst = instance_from_handle(pd);
|
||||
inst->GetPhysicalDeviceExternalFenceProperties(pd, info, p);
|
||||
layer_log("trace: vkGetPhysicalDeviceExternalFenceProperties: handle 0x%x -> features 0x%x", (unsigned)info->handleType,
|
||||
(unsigned)p->externalFenceFeatures);
|
||||
}
|
||||
|
||||
void trace_extension_names(const VkExtensionProperties *ext, uint32_t n)
|
||||
{
|
||||
char line[400];
|
||||
size_t o = 0;
|
||||
line[0] = 0;
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
size_t len = strlen(ext[i].extensionName);
|
||||
if (o && o + len + 2 > sizeof(line)) {
|
||||
layer_log("trace: extensions:%s", line);
|
||||
o = 0;
|
||||
line[0] = 0;
|
||||
}
|
||||
o += (size_t)snprintf(line + o, sizeof(line) - o, " %s", ext[i].extensionName);
|
||||
}
|
||||
if (o) layer_log("trace: extensions:%s", line);
|
||||
}
|
||||
|
||||
void trace_load(struct instance *inst)
|
||||
{
|
||||
#define T_LOAD(name) inst->name = (PFN_vk##name)inst->gipa(inst->handle, "vk" #name);
|
||||
#define T_LOAD_KHR(name) \
|
||||
if (!inst->name) \
|
||||
inst->name = (PFN_vk##name)inst->gipa(inst->handle, "vk" #name "KHR");
|
||||
T_LOAD(EnumeratePhysicalDevices)
|
||||
T_LOAD(GetPhysicalDeviceMemoryProperties2)
|
||||
T_LOAD_KHR(GetPhysicalDeviceMemoryProperties2)
|
||||
T_LOAD(GetPhysicalDeviceQueueFamilyProperties)
|
||||
T_LOAD(GetPhysicalDeviceQueueFamilyProperties2)
|
||||
T_LOAD_KHR(GetPhysicalDeviceQueueFamilyProperties2)
|
||||
T_LOAD(GetPhysicalDeviceExternalBufferProperties)
|
||||
T_LOAD_KHR(GetPhysicalDeviceExternalBufferProperties)
|
||||
T_LOAD(GetPhysicalDeviceExternalSemaphoreProperties)
|
||||
T_LOAD_KHR(GetPhysicalDeviceExternalSemaphoreProperties)
|
||||
T_LOAD(GetPhysicalDeviceExternalFenceProperties)
|
||||
T_LOAD_KHR(GetPhysicalDeviceExternalFenceProperties)
|
||||
#undef T_LOAD
|
||||
#undef T_LOAD_KHR
|
||||
}
|
||||
|
||||
struct trace_hook {
|
||||
const char *name;
|
||||
PFN_vkVoidFunction function;
|
||||
size_t next_offset;
|
||||
};
|
||||
|
||||
#define TH(fn, field) {"vk" #fn, (PFN_vkVoidFunction)Trace##field, offsetof(struct instance, field)}
|
||||
|
||||
static const struct trace_hook trace_hooks[] = {
|
||||
TH(EnumeratePhysicalDevices, EnumeratePhysicalDevices),
|
||||
TH(GetPhysicalDeviceMemoryProperties, GetPhysicalDeviceMemoryProperties),
|
||||
TH(GetPhysicalDeviceMemoryProperties2, GetPhysicalDeviceMemoryProperties2),
|
||||
TH(GetPhysicalDeviceMemoryProperties2KHR, GetPhysicalDeviceMemoryProperties2),
|
||||
TH(GetPhysicalDeviceQueueFamilyProperties, GetPhysicalDeviceQueueFamilyProperties),
|
||||
TH(GetPhysicalDeviceQueueFamilyProperties2, GetPhysicalDeviceQueueFamilyProperties2),
|
||||
TH(GetPhysicalDeviceQueueFamilyProperties2KHR, GetPhysicalDeviceQueueFamilyProperties2),
|
||||
TH(GetPhysicalDeviceFormatProperties, GetPhysicalDeviceFormatProperties),
|
||||
TH(GetPhysicalDeviceFormatProperties2, GetPhysicalDeviceFormatProperties2),
|
||||
TH(GetPhysicalDeviceFormatProperties2KHR, GetPhysicalDeviceFormatProperties2),
|
||||
TH(GetPhysicalDeviceImageFormatProperties, GetPhysicalDeviceImageFormatProperties),
|
||||
TH(GetPhysicalDeviceImageFormatProperties2, GetPhysicalDeviceImageFormatProperties2),
|
||||
TH(GetPhysicalDeviceImageFormatProperties2KHR, GetPhysicalDeviceImageFormatProperties2),
|
||||
TH(GetPhysicalDeviceExternalBufferProperties, GetPhysicalDeviceExternalBufferProperties),
|
||||
TH(GetPhysicalDeviceExternalBufferPropertiesKHR, GetPhysicalDeviceExternalBufferProperties),
|
||||
TH(GetPhysicalDeviceExternalSemaphoreProperties, GetPhysicalDeviceExternalSemaphoreProperties),
|
||||
TH(GetPhysicalDeviceExternalSemaphorePropertiesKHR, GetPhysicalDeviceExternalSemaphoreProperties),
|
||||
TH(GetPhysicalDeviceExternalFenceProperties, GetPhysicalDeviceExternalFenceProperties),
|
||||
TH(GetPhysicalDeviceExternalFencePropertiesKHR, GetPhysicalDeviceExternalFenceProperties),
|
||||
};
|
||||
|
||||
PFN_vkVoidFunction trace_hook(struct instance *inst, const char *name)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(trace_hooks) / sizeof(trace_hooks[0]); i++) {
|
||||
if (strcmp(name, trace_hooks[i].name)) continue;
|
||||
void *next = *(void **)((char *)inst + trace_hooks[i].next_offset);
|
||||
return next ? trace_hooks[i].function : NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -473,6 +473,11 @@ int main(int argc, char **argv)
|
||||
vkGetPhysicalDeviceMemoryProperties(pd, &mp);
|
||||
for (uint32_t i = 0; i < mp.memoryTypeCount; i++)
|
||||
printf(" memory type %u: heap %u flags 0x%x\n", i, mp.memoryTypes[i].heapIndex, mp.memoryTypes[i].propertyFlags);
|
||||
int host_visible = 0;
|
||||
for (uint32_t i = 0; i < mp.memoryTypeCount; i++)
|
||||
if (mp.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) host_visible = 1;
|
||||
/* The server offers HOST_VISIBLE only through a memory-sharing method that passed its self-test. */
|
||||
check(host_visible, "memory sharing", host_visible ? "" : "none (no host-visible memory type; the server found no way to share memory with this driver)");
|
||||
uint32_t nq = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(pd, &nq, NULL);
|
||||
VkQueueFamilyProperties qp[16];
|
||||
|
||||
Reference in new issue
Block a user