From 08592c9f5916b64dfc70aba9e67a74a764bb3ef5 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 19 Jul 2026 10:09:53 +0300 Subject: [PATCH 1/2] feat(wgpu): add Android native-window surfaces --- CHANGELOG.md | 8 ++++++ wgpu/surface_android.go | 42 +++++++++++++++++++++++++++++++ wgpu/surface_android_test.go | 5 ++++ wgpu/surface_android_wire.go | 25 ++++++++++++++++++ wgpu/surface_android_wire_test.go | 36 ++++++++++++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 wgpu/surface_android.go create mode 100644 wgpu/surface_android_test.go create mode 100644 wgpu/surface_android_wire.go create mode 100644 wgpu/surface_android_wire_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a867dc..e187d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Android `ANativeWindow*` surface creation through + `Instance.CreateSurfaceFromAndroidNativeWindow`, including v29 wire-layout + and zero-handle validation tests. + ## v0.5.3 (2026-07-12) ### Changed diff --git a/wgpu/surface_android.go b/wgpu/surface_android.go new file mode 100644 index 0000000..a2d0acd --- /dev/null +++ b/wgpu/surface_android.go @@ -0,0 +1,42 @@ +//go:build android + +package wgpu + +import ( + "runtime" + "unsafe" +) + +// CreateSurfaceFromAndroidNativeWindow creates a surface from an +// ANativeWindow. The caller must keep its ANativeWindow reference alive until +// the returned Surface is released. +func (inst *Instance) CreateSurfaceFromAndroidNativeWindow(window uintptr) (*Surface, error) { + if err := checkInit(); err != nil { + return nil, err + } + if inst == nil || inst.handle == 0 { + return nil, &WGPUError{Op: "CreateSurface", Message: "instance is nil or released"} + } + + source, err := newSurfaceSourceAndroidNativeWindow(window) + if err != nil { + return nil, err + } + desc := surfaceDescriptor{ + nextInChain: uintptr(unsafe.Pointer(&source)), + label: EmptyStringView(), + } + + handle, _, _ := procInstanceCreateSurface.Call( + inst.handle, + uintptr(unsafe.Pointer(&desc)), + ) + runtime.KeepAlive(&source) + runtime.KeepAlive(&desc) + if handle == 0 { + return nil, &WGPUError{Op: "CreateSurface", Message: "failed to create surface"} + } + + trackResource(handle, "Surface") + return &Surface{handle: handle}, nil +} diff --git a/wgpu/surface_android_test.go b/wgpu/surface_android_test.go new file mode 100644 index 0000000..113912f --- /dev/null +++ b/wgpu/surface_android_test.go @@ -0,0 +1,5 @@ +//go:build android + +package wgpu + +var _ func(*Instance, uintptr) (*Surface, error) = (*Instance).CreateSurfaceFromAndroidNativeWindow diff --git a/wgpu/surface_android_wire.go b/wgpu/surface_android_wire.go new file mode 100644 index 0000000..de12791 --- /dev/null +++ b/wgpu/surface_android_wire.go @@ -0,0 +1,25 @@ +package wgpu + +// surfaceSourceAndroidNativeWindow matches +// WGPUSurfaceSourceAndroidNativeWindow in the WebGPU native v29 header. +// It is host-buildable so ordinary CI can verify the Android ABI layout. +type surfaceSourceAndroidNativeWindow struct { + chain ChainedStruct + window uintptr // ANativeWindow* +} + +func newSurfaceSourceAndroidNativeWindow(window uintptr) (surfaceSourceAndroidNativeWindow, error) { + if window == 0 { + return surfaceSourceAndroidNativeWindow{}, &WGPUError{ + Op: "CreateSurface", + Message: "Android native window is nil", + } + } + + return surfaceSourceAndroidNativeWindow{ + chain: ChainedStruct{ + SType: uint32(STypeSurfaceSourceAndroidNativeWindow), + }, + window: window, + }, nil +} diff --git a/wgpu/surface_android_wire_test.go b/wgpu/surface_android_wire_test.go new file mode 100644 index 0000000..1cfd4ee --- /dev/null +++ b/wgpu/surface_android_wire_test.go @@ -0,0 +1,36 @@ +package wgpu + +import ( + "testing" + "unsafe" +) + +func TestABISurfaceSourceAndroidNativeWindow(t *testing.T) { + source, err := newSurfaceSourceAndroidNativeWindow(0x1234) + if err != nil { + t.Fatalf("newSurfaceSourceAndroidNativeWindow: %v", err) + } + + if got := unsafe.Sizeof(source); got != 24 { + t.Fatalf("sizeof(surfaceSourceAndroidNativeWindow) = %d, want 24", got) + } + if got := unsafe.Offsetof(source.window); got != 16 { + t.Fatalf("offsetof(window) = %d, want 16", got) + } + if source.chain.Next != 0 { + t.Fatalf("chain.Next = %#x, want 0", source.chain.Next) + } + if source.chain.SType != uint32(STypeSurfaceSourceAndroidNativeWindow) { + t.Fatalf("chain.SType = %#x, want %#x", source.chain.SType, uint32(STypeSurfaceSourceAndroidNativeWindow)) + } + if source.window != 0x1234 { + t.Fatalf("window = %#x, want 0x1234", source.window) + } +} + +func TestSurfaceSourceAndroidNativeWindowRejectsZero(t *testing.T) { + _, err := newSurfaceSourceAndroidNativeWindow(0) + if err == nil { + t.Fatal("newSurfaceSourceAndroidNativeWindow(0) succeeded") + } +} From 3ef898059d1257ef9a7f9810b92e4fc5f5fb0c58 Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 22 Jul 2026 19:14:50 +0300 Subject: [PATCH 2/2] style(wgpu): align Android surface wire patterns --- wgpu/surface_android.go | 3 --- wgpu/surface_android_wire.go | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/wgpu/surface_android.go b/wgpu/surface_android.go index a2d0acd..550ad00 100644 --- a/wgpu/surface_android.go +++ b/wgpu/surface_android.go @@ -3,7 +3,6 @@ package wgpu import ( - "runtime" "unsafe" ) @@ -31,8 +30,6 @@ func (inst *Instance) CreateSurfaceFromAndroidNativeWindow(window uintptr) (*Sur inst.handle, uintptr(unsafe.Pointer(&desc)), ) - runtime.KeepAlive(&source) - runtime.KeepAlive(&desc) if handle == 0 { return nil, &WGPUError{Op: "CreateSurface", Message: "failed to create surface"} } diff --git a/wgpu/surface_android_wire.go b/wgpu/surface_android_wire.go index de12791..7c278a5 100644 --- a/wgpu/surface_android_wire.go +++ b/wgpu/surface_android_wire.go @@ -4,8 +4,8 @@ package wgpu // WGPUSurfaceSourceAndroidNativeWindow in the WebGPU native v29 header. // It is host-buildable so ordinary CI can verify the Android ABI layout. type surfaceSourceAndroidNativeWindow struct { - chain ChainedStruct - window uintptr // ANativeWindow* + chain ChainedStruct // 16 bytes: next (8) + sType (4) + padding (4) + window uintptr // 8 bytes - ANativeWindow* } func newSurfaceSourceAndroidNativeWindow(window uintptr) (surfaceSourceAndroidNativeWindow, error) { @@ -18,6 +18,7 @@ func newSurfaceSourceAndroidNativeWindow(window uintptr) (surfaceSourceAndroidNa return surfaceSourceAndroidNativeWindow{ chain: ChainedStruct{ + Next: 0, SType: uint32(STypeSurfaceSourceAndroidNativeWindow), }, window: window,