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:
The412Banner committed 2026-10-15 18:53:40 -04:00
1 parent 521d666046
commit a4d47cf33e
21 files changed
+646 -25

No files matched your search

+148 -4
View File
@@ -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;
+73
View File
@@ -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);
+2
View File
@@ -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)
},
+2
View File
@@ -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
+10 -1
View File
@@ -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++) {
+12
View File
@@ -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);
+233
View File
@@ -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;
}
+5
View File
@@ -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];