# Android Vulkan preview Android/arm64 support is an unreleased implementation preview on WGPU `main`, not a released support claim. It landed in [gogpu/wgpu#273](https://github.com/gogpu/wgpu/pull/273), whose squash included the Android WSI work prepared and discussed in [gogpu/wgpu#268](https://github.com/gogpu/wgpu/pull/268). Its current scope is Vulkan, arm64, Android API 29 or newer, and both `CGO_ENABLED=0` and `CGO_ENABLED=1`. GLES, software fallback, 32-bit Android, debug callbacks, and API 28 or older are out of scope. The default backend consumes canonical [goffi v0.6.1](https://github.com/go-webgpu/goffi/releases/tag/v0.6.1), released from [go-webgpu/goffi#62](https://github.com/go-webgpu/goffi/pull/62). The `rust` build-tag path also depends on canonical [go-webgpu/webgpu#24](https://github.com/go-webgpu/webgpu/pull/24), merged at `a801aed7399042e5564ef76fc9f075da5cb70081`. goffi is declared directly in `go.mod` and verified by `go.sum`; the merged but unreleased WebGPU source is injected through an ephemeral workspace. There is no forked module path, `replace` directive, or committed `go.work`. API 29 is the native symbol and application install floor. API 36 is the downstream Java compile/Play target and the newest runtime-observer endpoint; it is not a distinct WGPU ABI. NDK r29 publishes native platform wrappers through API 35, so the native cross-build deliberately targets the supported API 29 floor instead of inventing an API 36 clang target. Policy tests cover the API 29 branch and the shared API 30-through-36 branch. ## Host contract New Android hosts should use the explicit raw-target path: ```go surface, err := instance.CreateSurfaceUnsafe( wgpu.SurfaceTargetFromAndroidNativeWindow(nativeWindow), ) ``` `nativeWindow` is the raw, non-null `ANativeWindow*` value. Android has no display handle for this operation. The existing `Instance.CreateSurface(displayHandle, windowHandle)` method remains a source-compatible adapter and ignores `displayHandle`, matching Rust `wgpu` v29. Hosts that represent native-window ownership with a Go object can instead implement `SurfaceTarget` and call `CreateSurfaceFromTarget`. WGPU samples the provider once and retains it until backend surface destruction completes. See [Surface targets](SURFACE-TARGETS.md) for the complete safe/unsafe mapping. The host owns the Activity/native-window lifecycle. For the unsafe path it must keep its application reference valid until `Surface.Release` returns. A successfully created `VkSurfaceKHR` owns Vulkan's separate window reference until `vkDestroySurfaceKHR`; WGPU therefore does not call `ANativeWindow_acquire` or `ANativeWindow_release` itself. Surfaces are independent. Creating a second surface does not invalidate the first. When Android replaces a native window, create a surface for the new raw window and release the old surface after its configured device has drained. There is deliberately no display-generation protocol: a counter cannot prove pointer lifetime and would reject legitimate simultaneous surfaces. ## Rust wgpu v29 mapping The semantic oracle is gfx-rs/wgpu v29.0.3 commit `4cbe6232b2d7c289b6e1a38416a6ae1461a22e81`. | Behavior | Rust v29 location | Go implementation and proof | |----------|-------------------|-----------------------------| | Safe provider is retained for the surface lifetime | `wgpu/src/api/{instance,surface}.rs` | `SurfaceTarget`, `CreateSurfaceFromTarget`, release-order test | | Unsafe raw target retains no ownership source | `SurfaceTargetUnsafe::RawHandle`, `create_surface_unsafe` | opaque constructors and `CreateSurfaceUnsafe` validation tests | | Target kind remains explicit below the public API | `raw-window-handle` enum matching | typed `hal.SurfaceTarget`; backend kind-routing tests | | Create for every enabled backend; succeed if any succeeds | `wgpu-core/src/instance.rs::create_surface` | per-backend HAL surface map and matching adapter-qualification tests | | Ignore Android display; use only raw `a_native_window` | `wgpu-hal/src/vulkan/instance.rs` | `api_android.go`, `android_surface_policy_test.go` | | Load `libvulkan.so`; require Android WSI extension/commands | Vulkan instance loader | `vk/loader.go`, `vk/commands.go`, source-selection audit | | API 29 uses infinite acquire timeout; API 30+ preserves one second | `swapchain/native.rs::acquire` | `swapchainPlatformPolicy.acquireTimeout` | | Fence wait uses `waitAll=true` | `swapchain/native.rs::acquire` | checked acquire path in `swapchain.go` | | Identity pre-transform; ignore orientation-only `SUBOPTIMAL` | `swapchain/native.rs::{create_swapchain,acquire,present}` | platform policy and host tests | | Fixed `currentExtent` is authoritative; variable extent is clamped | `swapchain/native.rs::surface_capabilities` | `selectSwapchainExtent` tests | | `Rgba16Float` pairs with extended-linear sRGB | `swapchain/native.rs::create_swapchain`, `conv.rs` | exact pair-selection test | | Device drains configured swapchains before native teardown | native swapchain ownership | [wgpu#269](https://github.com/gogpu/wgpu/pull/269) lifecycle tests | This change adds no Android-driven global Vulkan version check or adapter filter. The canonical backend's existing application-version request remains unchanged; actual extensions, commands, features, and device evidence decide whether a Vulkan implementation works. ## Integration status The five WGPU prerequisites (#264, #265, #266, #267, and #269), goffi#62/v0.6.1, webgpu#24, and typed-surface PR #273 are merged. The #273 squash also included the Android-owned commits originally carried by #268, so WGPU `main` already contains both the Vulkan WSI implementation and the typed public/HAL surface API. PR #268 remains the implementation's design and review record rather than a code dependency. [wgpu#253](https://github.com/gogpu/wgpu/pull/253) is the design precedent for matching Rust's typed public/HAL seam; it is not a code dependency of Android. The default Vulkan path uses goffi v0.6.1. The `rust` build-tag path needs the canonical `go-webgpu/webgpu` helper as well. No WGPU-local copy of that helper should be added. ## Reproducing deterministic proof Use Android NDK r29 and clean checkouts of this branch and the canonical merged `go-webgpu/webgpu` helper: ```bash WEBGPU_DIR=/path/to/go-webgpu-webgpu \ WEBGPU_EXPECTED_HEAD=a801aed7399042e5564ef76fc9f075da5cb70081 \ WEBGPU_EXPECTED_PATCH=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ ANDROID_NDK_HOME=/path/to/android-ndk-r29 \ GOTOOLCHAIN=go1.26.5 \ ./scripts/check-android-arm64-preview.sh ``` The script first asserts that the module graph selects canonical goffi v0.6.1. It then verifies Android-only source selection; compiles every package and test for both the default and `rust` implementations in cgo0 and cgo1 modes; builds each headless example; checks the generated Go and NDK C ABI layouts; and audits both ELF dependency sets. It requires Bionic `libc.so`/`libdl.so`, confirms `libvulkan.so` for the default backend and `libwgpu_native.so` for the Rust backend, and rejects glibc sonames, standalone `libpthread`, desktop WSI, GLES, and software fallback. Current CI repeats both lanes with Go 1.25.12 and Go 1.26.5 against canonical goffi v0.6.1 and the exact clean webgpu#24 head. These are deterministic cross-build and binary-shape checks. They do not prove process startup, adapter enumeration, surface creation, rendering, presentation, rotation, or lifecycle recovery on a physical device. Before the preview can become a released support claim, this WGPU integration and a canonical `go-webgpu/webgpu` release containing the Android helper must ship; API 29 plus API 36 arm64 devices must also pass startup, known-color presentation, rotation, Activity recreation, and repeated native-window replacement without crashes, hangs, stale frames, or validation errors.