Skip to content

feat(wgpu): add Android native-window surface source#24

Merged
kolkov merged 2 commits into
go-webgpu:mainfrom
besmpl:besmpl/android-surface-source
Jul 22, 2026
Merged

feat(wgpu): add Android native-window surface source#24
kolkov merged 2 commits into
go-webgpu:mainfrom
besmpl:besmpl/android-surface-source

Conversation

@besmpl

@besmpl besmpl commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Add the missing wgpu-native v29 Android surface-source wrapper:

  • expose Instance.CreateSurfaceFromAndroidNativeWindow on Android;
  • build WGPUSurfaceSourceAndroidNativeWindow with the existing v29 SType;
  • reject a zero ANativeWindow* before FFI;
  • keep the wire descriptor alive for the native call; and
  • verify the AArch64 wire size, field offset, chain tag, and Android method surface.

Why

The bindings already expose STypeSurfaceSourceAndroidNativeWindow, but there is no constructor that can pass the corresponding chained descriptor to wgpuInstanceCreateSurface. That leaves Android consumers unable to use wgpu-native through the canonical Go wrapper.

This is the Rust-wrapper prerequisite for gogpu/wgpu#273. The #273 description records its current exact integration head; its CI pins this PR at exact helper head 08592c9f5916b64dfc70aba9e67a74a764bb3ef5 through the canonical PR ref. After #24 merges and is released, #273 can consume the canonical version. The pure-Go Vulkan implementation in gogpu/wgpu#268 does not depend on this helper; only the optional -tags rust Android path does.

Ownership boundary

The caller owns its ANativeWindow reference and must keep it valid until the returned surface is released. This method neither acquires nor releases the window, and it adds no Activity, JNI, packaging, cgo, or nativeexport policy.

Android API 29 is a downstream install/native floor and API 36 is a downstream application compile/target choice; the wgpu-native wire descriptor itself is API-level independent.

Validation

  • GOWORK=off go test ./wgpu -run 'Test(ABISurfaceSourceAndroidNativeWindow|SurfaceSourceAndroidNativeWindowRejectsZero)$'
  • GOOS=android GOARCH=arm64 CGO_ENABLED=0 GOWORK=off go test -exec=true ./wgpu
  • git diff --check

The full downstream WGPU Android matrix on the current #273 branch compiles every package and test through this exact helper head in both cgo modes and audits the resulting Rust-wrapper ELF dependency on libwgpu_native.so.

@besmpl

besmpl commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

@kolkov — the only missing readiness signal here is approval of the fork workflow run: https://github.com/go-webgpu/webgpu/actions/runs/29677865347. GitHub currently records it as action_required with zero jobs, so Codecov has no head or patch report to evaluate.

As a preflight at exact head 08592c9f5916b64dfc70aba9e67a74a764bb3ef5, I reran the workflow’s Go 1.25.12 coverage command with the same official wgpu-native v29 fixture; it passes at 32.8% total coverage (the current base is 26.28%). Could you please approve the existing run? I will address any real CI or Codecov finding immediately once the authoritative hosted report exists.

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-structured PR. The wire struct ABI is verified correct against webgpu.h, the build tag strategy (wire struct without tag for host testing) is actually an improvement over our existing surface files, and the zero-handle validation + error handling follow the established pattern.

A few things to align before merge:


1. runtime.KeepAlive — pattern divergence (discussion needed)

surface_android.go adds runtime.KeepAlive(&source) and runtime.KeepAlive(&desc) after the .Call(). None of the existing surface files (surface_windows.go, surface_linux.go, surface_darwin.go) use KeepAlive.

Both source and desc are stack-allocated variables. Per Go GC rules, stack variables are GC roots and cannot be collected while the frame is alive. goffi v0.4.1+ also added internal KeepAlive for call arguments (see goffi CHANGELOG).

The KeepAlive calls are technically harmless (compiler fence, no-op for stack data), but they create an inconsistency. Two options:

  • Remove them from this PR to match existing surface files, or
  • Keep them and add a follow-up PR applying the same pattern to all surface_*.go files

I'm fine with either — just want a conscious decision rather than silent divergence.

2. Minor style: explicit Next: 0 and inline size comments

All existing wire struct constructors explicitly initialize Next: 0:

// surface_windows.go pattern:
chain: ChainedStruct{
    Next:  0,
    SType: uint32(STypeSurfaceSourceWindowsHWND),
},

And wire struct definitions have inline size comments:

// surface_darwin.go pattern:
type surfaceSourceMetalLayer struct {
    chain ChainedStruct // 16 bytes: next (8) + sType (4) + padding (4)
    layer uintptr       // 8 bytes - CAMetalLayer*
}

Please add both for consistency:

type surfaceSourceAndroidNativeWindow struct {
    chain  ChainedStruct // 16 bytes: next (8) + sType (4) + padding (4)
    window uintptr       // 8 bytes - ANativeWindow*
}
chain: ChainedStruct{
    Next:  0,
    SType: uint32(STypeSurfaceSourceAndroidNativeWindow),
},

3. What the PR does well

  • Separate wire file without build tag — lets CI verify ABI layout without Android toolchain. This is better than our current approach where wire structs are locked behind platform build tags. We should consider adopting this pattern for future surface additions.
  • var _ func(*Instance, uintptr) (*Surface, error) signature assertion — clean compile-time API contract check.
  • Ownership documentation in godoc — "The caller must keep its ANativeWindow reference alive until the returned Surface is released" is exactly the right level of documentation for an unsafe ownership boundary.
  • ABI test thoroughness — sizeof, offsetof, chain.Next, chain.SType, window value — all verified.

Summary

Check Status
Wire struct matches WGPUSurfaceSourceAndroidNativeWindow in webgpu.h ✅ sizeof=24, offset(window)=16
SType matches WGPUSType_SurfaceSourceAndroidNativeWindow (0x8)
Error handling (nil instance, zero window, zero handle)
Build tag strategy (wire=host, method=android) ✅ Improvement
Cross-compile GOOS=android GOARCH=arm64 CGO_ENABLED=0
Pattern consistency with existing surface files ⚠️ Minor deviations (KeepAlive, Next:0, size comments)

Approve with the style alignment above. CI workflow run approved — let's see the Codecov report.

@kolkov kolkov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style alignment confirmed — KeepAlive removed, Next: 0 explicit, inline size comments added. Pattern now matches existing surface files. LGTM.

@kolkov
kolkov merged commit a801aed into go-webgpu:main Jul 22, 2026
@kolkov kolkov mentioned this pull request Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants