Android/arm64 support is an unreleased implementation preview on WGPU main,
not a released support claim. It landed in
gogpu/wgpu#273, whose squash included
the Android WSI work prepared and discussed in
gogpu/wgpu#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, released
from go-webgpu/goffi#62. The
rust build-tag path also depends on canonical
go-webgpu/webgpu#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.
New Android hosts should use the explicit raw-target path:
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 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.
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 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.
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 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.
Use Android NDK r29 and clean checkouts of this branch and the canonical merged
go-webgpu/webgpu helper:
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.shThe 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.