From 1dda3ae455645aef33e6fcc9e9dffdddcb722b05 Mon Sep 17 00:00:00 2001 From: Dylan Neve Date: Thu, 29 Oct 2026 18:50:56 +0000 Subject: [PATCH] Vulkan for the guest: gfxstream's Vulkan in SCGPU, and the bundle carries the guest's Vulkan scgpu.c: the renderer starts with USE_VK and the features VirtioGpuNext:disabled, VulkanEnsureCachedCoherentMemoryAvailable:disabled (the guest copies host-visible memory on flush and invalidate: hackios-xnu-kernel tools/gfxstream/VULKAN.md), and answers capset 1 with GFXSTREAM_VULKAN. debug.scion.vk=0 turns it off; a renderer that will not start with Vulkan starts as before, GL alone. ios-bundle: the macOS job builds libhakios.vk.dylib and vk_test (mk-vk.sh ios), the bundle's root gets them (build.sh VK_DIR=), and rc.jobs runs vk_test once at boot. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ios-bundle.yml | 28 +++++++++++++++++++-- app/src/main/cpp/scgpu.c | 42 ++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ios-bundle.yml b/.github/workflows/ios-bundle.yml index 6a8c51a..604108e 100644 --- a/.github/workflows/ios-bundle.yml +++ b/.github/workflows/ios-bundle.yml @@ -74,6 +74,24 @@ jobs: - name: Install host build tools (Homebrew) run: brew install meson ninja pkgconf cmake + - name: The guest's Vulkan for iOS (hackios-xnu-kernel tools/gfxstream/mk-vk.sh ios; it needs Xcode's clang) + env: + KERNEL_REF: ${{ inputs.kernel_ref }} + run: | + git clone -q --filter=blob:none https://github.com/Scion-Project/hackios-xnu-kernel.git "$RUNNER_TEMP/k" + git -C "$RUNNER_TEMP/k" checkout -q "$KERNEL_REF" + VK_BUILD="$RUNNER_TEMP/vk" bash "$RUNNER_TEMP/k/tools/gfxstream/mk-vk.sh" ios + mkdir -p "$RUNNER_TEMP/vk-out" + cp "$RUNNER_TEMP/vk/out-ios/libhakios.vk.dylib" "$RUNNER_TEMP/vk/out-ios/vk_test" "$RUNNER_TEMP/vk-out/" + ls -la "$RUNNER_TEMP/vk-out" + + - uses: actions/upload-artifact@v4.6.0 + with: + name: scion-vk-for-bundle + path: ${{ runner.temp }}/vk-out + if-no-files-found: error + retention-days: 1 + - name: Point Gradle at the SDK run: echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties @@ -232,12 +250,18 @@ jobs: working-directory: /mnt/w/k run: avf/shim/build.sh && ls -la avf/shim/out/Image - - name: The small root and the system image (userland/build.sh GL=gfx REST=1) + - uses: actions/download-artifact@v4 + with: + name: scion-vk-for-bundle + path: /mnt/w/vk + + - name: The small root and the system image (userland/build.sh GL=gfx REST=1, with the guest's Vulkan) working-directory: /mnt/w/k run: | - (source ./xnu-env.sh && cd userland && GL=gfx REST=1 bash build.sh) > "$W/userland.log" 2>&1 || + (source ./xnu-env.sh && cd userland && GL=gfx REST=1 VK_DIR="$W/vk" bash build.sh) > "$W/userland.log" 2>&1 || { tail -100 "$W/userland.log"; exit 1; } tail -5 "$W/userland.log" + grep -a 'guest.s Vulkan' "$W/userland.log" || { echo "::error::the root has no Vulkan library"; exit 1; } ls -la userland/root.apfs userland/system.apfs - uses: actions/download-artifact@v4 diff --git a/app/src/main/cpp/scgpu.c b/app/src/main/cpp/scgpu.c index ee9bd3a..85ac747 100644 --- a/app/src/main/cpp/scgpu.c +++ b/app/src/main/cpp/scgpu.c @@ -128,9 +128,21 @@ struct sr_fence { uint32_t flags; uint64_t fence_id; uint32_t ctx_id; uint8_t ri #define SR_PARAM_FENCE_CALLBACK 3 #define SR_PARAM_WIN0_WIDTH 4 #define SR_PARAM_WIN0_HEIGHT 5 +#define SR_PARAM_RENDERER_FEATURES 11 #define SR_FLAGS_USE_EGL (1u << 0) #define SR_FLAGS_SURFACELESS (1u << 3) #define SR_FLAGS_USE_GLES (1u << 4) +#define SR_FLAGS_USE_VK (1u << 5) +#define CAPSET_GFXSTREAM_VULKAN 3 + +/* + * Vulkan for the guest (hackios-xnu-kernel tools/gfxstream/VULKAN.md): gfxstream's Vulkan decoder on the phone's + * own driver, the guest's Vulkan stream on the same pipe as its GL. The guest cannot map host memory here, so + * host-visible memory is a guest copy moved on flush and invalidate: VirtioGpuNext off, and no insistence on a + * coherent cached memory type, which that leaves none of. On by default; debug.scion.vk=0 turns it off, and a + * renderer that will not start with it starts as before, GL alone. + */ +static const char vk_features[] = "VirtioGpuNext:disabled,VulkanEnsureCachedCoherentMemoryAvailable:disabled"; static struct { int (*init)(struct sr_param *, uint64_t); @@ -159,7 +171,7 @@ static struct { volatile struct scgpu_header *h; int fb_w, fb_h; pthread_t thread; - int running, renderer_up; + int running, renderer_up, vulkan; volatile int stop; pthread_mutex_t window_lock; ANativeWindow *window; @@ -348,14 +360,15 @@ handle_cmd(const uint8_t *cmd, uint32_t len, uint32_t max, uint8_t *resp) struct resp_capset_info r; NEED(c); memcpy(&c, cmd, sizeof(c)); - if (c.capset_index != 0) { - err = -ENOENT; /* one capset: gfxstream's GLES */ + /* gfxstream's GLES, then its Vulkan when the renderer has it */ + if (c.capset_index > (S.vulkan ? 1u : 0u)) { + err = -ENOENT; break; } memset(&r, 0, sizeof(r)); r.hdr.type = RESP_OK_CAPSET_INFO; - r.capset_id = CAPSET_GFXSTREAM_GLES; - sr.get_cap_set(CAPSET_GFXSTREAM_GLES, &r.capset_max_version, &r.capset_max_size); + r.capset_id = c.capset_index == 0 ? CAPSET_GFXSTREAM_GLES : CAPSET_GFXSTREAM_VULKAN; + sr.get_cap_set(r.capset_id, &r.capset_max_version, &r.capset_max_size); memcpy(resp, &r, sizeof(r)); return sizeof(r); } @@ -857,19 +870,32 @@ Java_io_github_dylanneve1_iosonpixel_avf_ScgpuNative_start(JNIEnv *env, jclass c return -1; } if (!S.renderer_up) { + char vk_prop[PROP_VALUE_MAX] = ""; + int vk = !(__system_property_get("debug.scion.vk", vk_prop) > 0 && vk_prop[0] == '0'); struct sr_param params[] = { { SR_PARAM_USER_DATA, 0 }, - { SR_PARAM_RENDERER_FLAGS, SR_FLAGS_USE_EGL | SR_FLAGS_SURFACELESS | SR_FLAGS_USE_GLES }, + { SR_PARAM_RENDERER_FLAGS, SR_FLAGS_USE_EGL | SR_FLAGS_SURFACELESS | SR_FLAGS_USE_GLES | + (vk ? SR_FLAGS_USE_VK : 0) }, { SR_PARAM_FENCE_CALLBACK, (uint64_t)(uintptr_t)fence_cb }, { SR_PARAM_WIN0_WIDTH, (uint64_t)fb_w }, { SR_PARAM_WIN0_HEIGHT, (uint64_t)fb_h }, + { SR_PARAM_RENDERER_FEATURES, (uint64_t)(uintptr_t)vk_features }, /* last: dropped without Vulkan */ }; - int rc = sr.init(params, sizeof(params) / sizeof(params[0])); + uint64_t n = sizeof(params) / sizeof(params[0]) - (vk ? 0 : 1); + int rc = sr.init(params, n); + if (rc != 0 && vk) { + ERR("stream_renderer_init with Vulkan: %d; starting it again for GL alone", rc); + vk = 0; + params[1].value &= ~(uint64_t)SR_FLAGS_USE_VK; + rc = sr.init(params, n - 1); + } if (rc != 0) { ERR("stream_renderer_init: %d", rc); return -4; } + LOG("renderer up: GL%s", vk ? " and Vulkan" : " (Vulkan off: debug.scion.vk=0, or it would not start)"); S.renderer_up = 1; + S.vulkan = vk; } S.h = (volatile struct scgpu_header *)S.base; memset(S.base, 0, SCGPU_POOL_OFF); @@ -877,7 +903,7 @@ Java_io_github_dylanneve1_iosonpixel_avf_ScgpuNative_start(JNIEnv *env, jclass c S.h->version = SCGPU_VERSION; S.h->size = S.size; S.h->features_lo = (1u << F_VIRGL) | (1u << F_CONTEXT_INIT); - S.h->num_capsets = 1; + S.h->num_capsets = S.vulkan ? 2 : 1; S.commands = S.errors = S.busy_ns = 0; S.rings_in = S.rings_out = S.sleeps = 0; S.records = S.record_bytes = 0;