Iris: "remove both decisions and iris.md. I've decided to instead make decisions when planning with agents rather than after they do things, and they're both too long for me to wanna read, + don't cover all the decisions I'll wanna make about the code anyways. I'll just naturally run into things for now. Todo is important though." So docs/DECISIONS.md (850 lines) and docs/IRIS.md (1,986) are gone, and AGENTS.md now says not to start another: raise a choice while planning it with her, otherwise decide it and put the reasoning at the code it governs. The TODO lists stay. docs/SUBAGENTS_DECISIONS.md went with them -- same artefact, same reasoning, and she did not name it, so its six decisions were folded into docs/SUBAGENTS.md rather than deleted. Deleting the logs left ~30 citations dangling in code comments and docs. Each states its reason inline and cited the file only for provenance, so they now read "decided 2026-09-07" or name the module doc that carries the reasoning. The root had six things that were not a program or a document. Moved, per "I only meant top level sh files": run-tests.sh, test-wg-tunnel.sh, wg-setup-host.sh -> scripts/ rigs/ -> scripts/rigs/ xtask/ -> scripts/xtask/ A project's own scripts stayed with the project: app/*.sh, app-rust/*.sh, iris/*.sh and server/enroll-link.sh did not move. `target/` at the root is deleted and cannot come back: there was never a workspace there, and the 29 MB was only xtask's scratch space, now in scripts/xtask/target/. `cargo xtask apk` still runs from the repo root and now publishes to scripts/build/outputs/apk/<mode>/ -- one directory deep, because that is what Dev Updater's `*/build/outputs/apk/*/*.apk` discovery pattern needs, and scripts/xtask/build would have been two. Verified: ./scripts/run-tests.sh and `cd iris && cargo test` green, clippy and fmt clean everywhere, `cargo xtask apk debug --abi x86_64` builds and signs an APK carrying lib/x86_64/libai_app.so at the new publish path, and the repo root is now eleven entries with no build output among them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
134 lines
5.3 KiB
C
134 lines
5.3 KiB
C
// What this VM's virtio-gpu actually offers, asked of the kernel and the
|
|
// driver rather than inferred from the host's qemu command line.
|
|
//
|
|
// cc -O2 -o virtgpu-probe virtgpu-probe.c -I/usr/include/libdrm -ldrm -lvulkan
|
|
// ./virtgpu-probe
|
|
//
|
|
// Written 2026-09-08 for the question "Venus keeps causing problems, is
|
|
// there something to do with qemu instead" (docs/RUST.md, "Venus went
|
|
// away for an hour"). Three things, each of which was guessed wrong at
|
|
// least once before being measured:
|
|
//
|
|
// 1. Which capsets the host offers. Capset 6 (DRM) is the "native
|
|
// context" one -- RADV running in the guest against a passed-through
|
|
// DRM context instead of Venus proxying every Vulkan call. Whether
|
|
// it is available is a host-side fact this is the only way to read
|
|
// from in here.
|
|
// 2. Whether the Vulkan device has compute. It does, and assuming it
|
|
// did not sent one investigation down the wrong path: the "no
|
|
// compute" finding on record is about the *Android emulator's*
|
|
// SwiftShader GL path, a different machine entirely.
|
|
// 3. Whether plain Vulkan device teardown crashes on this adapter. It
|
|
// does not -- which is what makes wgpu's teardown SIGSEGV wgpu's and
|
|
// not the driver's, and is the kind of claim that is worthless
|
|
// without the negative half.
|
|
//
|
|
// C rather than a Rust crate on purpose: two of the three are ioctl and
|
|
// loader questions that a wgpu-shaped rig cannot ask, and this needs to
|
|
// keep working when the thing under suspicion is wgpu itself.
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <drm/virtgpu_drm.h>
|
|
#include <vulkan/vulkan.h>
|
|
#include <xf86drm.h>
|
|
|
|
static const char *capset_name(int id) {
|
|
switch (id) {
|
|
case 1: return "VIRGL";
|
|
case 2: return "VIRGL2";
|
|
case 3: return "GFXSTREAM_VULKAN";
|
|
case 4: return "VENUS";
|
|
case 5: return "CROSS_DOMAIN";
|
|
case 6: return "DRM (native context)";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
static int capsets(void) {
|
|
int fd = open("/dev/dri/renderD128", O_RDWR);
|
|
if (fd < 0) {
|
|
printf("capsets: cannot open /dev/dri/renderD128: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
struct drm_virtgpu_getparam gp;
|
|
uint64_t mask = 0;
|
|
memset(&gp, 0, sizeof gp);
|
|
gp.param = VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs;
|
|
gp.value = (uint64_t)(uintptr_t)&mask;
|
|
int rc = drmIoctl(fd, DRM_IOCTL_VIRTGPU_GETPARAM, &gp);
|
|
close(fd);
|
|
if (rc) {
|
|
// Not a virtio-gpu at all, or a kernel without the param: say
|
|
// which, rather than printing an empty list that reads like "the
|
|
// host offers nothing".
|
|
printf("capsets: SUPPORTED_CAPSET_IDs unavailable: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
printf("capsets: bitmask 0x%llx\n", (unsigned long long)mask);
|
|
for (int i = 1; i <= 8; i++)
|
|
if (mask & (1ull << i)) printf(" %d: %s\n", i, capset_name(i));
|
|
if (!(mask & (1ull << 6)))
|
|
printf(" (no capset 6: native context needs host-side virglrenderer + qemu support)\n");
|
|
return 0;
|
|
}
|
|
|
|
static int devices(void) {
|
|
VkInstanceCreateInfo ici = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
|
VkInstance inst;
|
|
if (vkCreateInstance(&ici, NULL, &inst) != VK_SUCCESS) {
|
|
printf("vulkan: no instance -- the loader found no usable ICD\n");
|
|
return 1;
|
|
}
|
|
uint32_t n = 0;
|
|
vkEnumeratePhysicalDevices(inst, &n, NULL);
|
|
if (n == 0) {
|
|
// The exact state this VM was in for an hour on 2026-09-08.
|
|
printf("vulkan: instance ok but ZERO devices -- the host refused a context\n");
|
|
vkDestroyInstance(inst, NULL);
|
|
return 1;
|
|
}
|
|
VkPhysicalDevice pd[8];
|
|
if (n > 8) n = 8;
|
|
vkEnumeratePhysicalDevices(inst, &n, pd);
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
VkPhysicalDeviceProperties p;
|
|
vkGetPhysicalDeviceProperties(pd[i], &p);
|
|
printf("vulkan: %s (api %u.%u.%u)\n", p.deviceName, VK_VERSION_MAJOR(p.apiVersion),
|
|
VK_VERSION_MINOR(p.apiVersion), VK_VERSION_PATCH(p.apiVersion));
|
|
printf(" compute: %u invocations/workgroup, size %u,%u,%u, %u bytes shared\n",
|
|
p.limits.maxComputeWorkGroupInvocations, p.limits.maxComputeWorkGroupSize[0],
|
|
p.limits.maxComputeWorkGroupSize[1], p.limits.maxComputeWorkGroupSize[2],
|
|
p.limits.maxComputeSharedMemorySize);
|
|
}
|
|
|
|
// The negative half of "wgpu's teardown crashes on Venus": five
|
|
// devices and the instance, created and destroyed the plain way.
|
|
float prio = 1.0f;
|
|
VkDeviceQueueCreateInfo q = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.queueFamilyIndex = 0, .queueCount = 1, .pQueuePriorities = &prio};
|
|
VkDeviceCreateInfo dci = {.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.queueCreateInfoCount = 1, .pQueueCreateInfos = &q};
|
|
for (int i = 0; i < 5; i++) {
|
|
VkDevice dev;
|
|
if (vkCreateDevice(pd[0], &dci, NULL, &dev) != VK_SUCCESS) {
|
|
printf("teardown: device %d could not be created\n", i);
|
|
return 1;
|
|
}
|
|
vkDestroyDevice(dev, NULL);
|
|
}
|
|
vkDestroyInstance(inst, NULL);
|
|
printf("teardown: 5 devices + instance created and destroyed cleanly\n");
|
|
return 0;
|
|
}
|
|
|
|
int main(void) {
|
|
int bad = capsets();
|
|
bad |= devices();
|
|
return bad;
|
|
}
|