From e9d3ea695c430a1f1e4714014c3628b32e1160e1 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Sun, 12 Jul 2026 13:31:50 +0300 Subject: [PATCH] =?UTF-8?q?deps:=20update=20goffi=20v0.5.2=20=E2=86=92=20v?= =?UTF-8?q?0.6.0,=20gputypes=20v0.5.0=20=E2=86=92=20v0.5.1,=20x/sys=20v0.4?= =?UTF-8?q?2.0=20=E2=86=92=20v0.47.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - goffi v0.6.0: assembly-level errno capture, callback stack safety fix, zero-alloc FFI - Adapt to breaking change: CallFunction returns (syscall.Errno, error) - Fix all golangci-lint issues: errcheck on Buffer.Unmap(), os.Remove, variable shadowing - Update CHANGELOG.md, UPSTREAM.md --- CHANGELOG.md | 19 +++++++++++++++++++ UPSTREAM.md | 7 ++++--- examples/buffer_introspection/main.go | 4 +++- examples/colored-triangle/main.go | 4 +++- examples/compute/main.go | 10 +++++++--- examples/cube/main.go | 4 +++- examples/error_handling/main.go | 12 ++++++------ examples/indirect/main.go | 8 ++++++-- examples/instanced/main.go | 8 ++++++-- examples/mrt/main.go | 4 +++- examples/rotating-triangle/main.go | 4 +++- examples/textured-quad/main.go | 8 ++++++-- examples/timestamp_query/main.go | 4 +++- examples/triangle/main.go | 4 ++-- go.mod | 6 +++--- go.sum | 12 ++++++------ internal/nativelib/download.go | 4 ++-- internal/nativelib/platform.go | 4 ++-- setup/setup.go | 6 +++--- wgpu/loader_unix.go | 2 +- 20 files changed, 91 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb173be..7a867dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ 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). +## v0.5.3 (2026-07-12) + +### Changed + +- **deps:** goffi v0.5.2 → v0.6.0 — assembly-level errno capture, callback stack safety fix, zero-allocation FFI +- **deps:** gputypes v0.5.0 → v0.5.1 +- **deps:** golang.org/x/sys v0.42.0 → v0.47.0 + +### Fixed + +- Adapt to goffi v0.6.0 breaking change: `CallFunction` now returns `(syscall.Errno, error)` +- Fix all golangci-lint issues: unchecked `Buffer.Unmap()` errors in examples, `os.Remove` in setup, variable shadowing + +### Highlights (from goffi v0.6.0) + +- **errno capture** — first pure-Go FFI with correct errno on Linux (captured in assembly trampoline, ~3-5ns overhead) +- **Callback stack fix** — `sync.Pool` + `runtime.Pinner` prevents goroutine stack move corruption during C callbacks +- **Zero-allocation FFI** — ABI-safe struct layout eliminates allocations in steady state + ## v0.5.2 (2026-05-27) ### Fixed diff --git a/UPSTREAM.md b/UPSTREAM.md index 624c50b..9559a1c 100644 --- a/UPSTREAM.md +++ b/UPSTREAM.md @@ -10,13 +10,14 @@ This document tracks upstream dependencies, pinned versions, and compatibility f |------------|---------|--------|------| | **wgpu-native** | [v29.0.0.0](https://github.com/gfx-rs/wgpu-native/releases/tag/v29.0.0.0) | — | 2026-04-10 | | **webgpu.h** | wgpu-native bundled | [`7d3186c`](https://github.com/webgpu-native/webgpu-headers/commit/7d3186c3dd2c708703524027b46b8703534ab3cc) | stable | -| **goffi** | [v0.5.0](https://github.com/go-webgpu/goffi/releases/tag/v0.5.0) | [`ca3231c`](https://github.com/go-webgpu/goffi/commit/ca3231c) | 2026-03-20 | -| **gputypes** | [v0.3.0](https://github.com/gogpu/gputypes/releases/tag/v0.3.0) | [`209398e`](https://github.com/gogpu/gputypes/commit/209398e) | 2026-03-20 | +| **goffi** | [v0.6.0](https://github.com/go-webgpu/goffi/releases/tag/v0.6.0) | [`895a3fa`](https://github.com/go-webgpu/goffi/commit/895a3fa) | 2026-07-12 | +| **gputypes** | [v0.5.1](https://github.com/gogpu/gputypes/releases/tag/v0.5.1) | [`239da90`](https://github.com/gogpu/gputypes/commit/239da90) | 2026-07-12 | ## Compatibility Matrix | go-webgpu | wgpu-native | goffi | gputypes | Go | |-----------|-------------|-------|----------|----| +| v0.5.3 | v29.0.0.0 | v0.6.0 | v0.5.1 | 1.25+ | | v0.5.0 | v29.0.0.0 | v0.5.0 | v0.3.0 | 1.25+ | | v0.4.3 | v27.0.4.0 | v0.5.0 | v0.3.0 | 1.25+ | | v0.4.2 | v27.0.4.0 | v0.4.2 | v0.2.0 | 1.25+ | @@ -111,4 +112,4 @@ Enum values in gputypes follow the webgpu.h specification. When gputypes updates --- -*Last updated: 2026-05-26 (v0.5.0)* +*Last updated: 2026-07-12 (v0.5.3)* diff --git a/examples/buffer_introspection/main.go b/examples/buffer_introspection/main.go index 8e580c9..f268022 100644 --- a/examples/buffer_introspection/main.go +++ b/examples/buffer_introspection/main.go @@ -77,7 +77,9 @@ func main() { fmt.Printf("Initial map state (MappedAtCreation): %s\n", mapStateToString(mapState)) // Unmap the buffer - mappableBuffer.Unmap() + if err := mappableBuffer.Unmap(); err != nil { + log.Printf("unmap: %v", err) + } // Check state after unmap mapState = mappableBuffer.MapState() diff --git a/examples/colored-triangle/main.go b/examples/colored-triangle/main.go index ff02719..2f21fa8 100644 --- a/examples/colored-triangle/main.go +++ b/examples/colored-triangle/main.go @@ -342,7 +342,9 @@ func (app *App) createVertexBuffer() error { copy(mappedSlice, vertices) // Unmap buffer to commit data to GPU - app.vertexBuffer.Unmap() + if err := app.vertexBuffer.Unmap(); err != nil { + log.Printf("unmap: %v", err) + } return nil } diff --git a/examples/compute/main.go b/examples/compute/main.go index 3463bbf..c7fce6d 100644 --- a/examples/compute/main.go +++ b/examples/compute/main.go @@ -24,7 +24,7 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { } ` -func main() { +func main() { //nolint:gocyclo,cyclop // example: sequential GPU setup is inherently linear // Initialize WebGPU if err := wgpu.Init(); err != nil { log.Fatal(err) @@ -94,7 +94,9 @@ func main() { mappedSlice := unsafe.Slice((*float32)(ptr), numElements) copy(mappedSlice, inputData) } - storageBuffer.Unmap() + if unmapErr := storageBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap storage buffer: %v", unmapErr) + } // Create readback buffer for results readbackBuffer, err := device.CreateBuffer(&wgpu.BufferDescriptor{ @@ -194,7 +196,9 @@ func main() { fmt.Println("All results correct!") } } - readbackBuffer.Unmap() + if unmapErr := readbackBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap readback buffer: %v", unmapErr) + } fmt.Println() fmt.Println("Key concepts demonstrated:") diff --git a/examples/cube/main.go b/examples/cube/main.go index 4147592..b570d50 100644 --- a/examples/cube/main.go +++ b/examples/cube/main.go @@ -448,7 +448,9 @@ func (app *App) createVertexBuffer() error { copy(mappedSlice, vertices) // Unmap buffer to commit data to GPU - app.vertexBuffer.Unmap() + if unmapErr := app.vertexBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap vertex buffer: %v", unmapErr) + } return nil } diff --git a/examples/error_handling/main.go b/examples/error_handling/main.go index 85203ad..9318654 100644 --- a/examples/error_handling/main.go +++ b/examples/error_handling/main.go @@ -45,7 +45,7 @@ func main() { fmt.Println("Queue obtained successfully") // Pop error scope and check for errors - errType, message := device.PopErrorScope(instance) + errType, message := device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API if errType != wgpu.ErrorTypeNoError { fmt.Printf("ERROR: Validation error occurred: %s\n", message) } else { @@ -64,11 +64,11 @@ func main() { fmt.Println("Performing GPU operations...") // Pop inner scope first (LIFO) - errType, message = device.PopErrorScope(instance) + errType, message = device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API fmt.Printf("Inner scope: errType=%v, message=%q\n", errType, message) // Pop outer scope - errType, message = device.PopErrorScope(instance) + errType, message = device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API fmt.Printf("Outer scope: errType=%v, message=%q\n", errType, message) // Example 3: Different error filters @@ -77,19 +77,19 @@ func main() { // Catch validation errors device.PushErrorScope(wgpu.ErrorFilterValidation) fmt.Println("Monitoring for validation errors...") - errType, _ = device.PopErrorScope(instance) + errType, _ = device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API fmt.Printf("Validation filter: errType=%v\n", errType) // Catch out-of-memory errors device.PushErrorScope(wgpu.ErrorFilterOutOfMemory) fmt.Println("Monitoring for out-of-memory errors...") - errType, _ = device.PopErrorScope(instance) + errType, _ = device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API fmt.Printf("Out-of-memory filter: errType=%v\n", errType) // Catch internal errors device.PushErrorScope(wgpu.ErrorFilterInternal) fmt.Println("Monitoring for internal errors...") - errType, _ = device.PopErrorScope(instance) + errType, _ = device.PopErrorScope(instance) //nolint:staticcheck // example demonstrates deprecated API fmt.Printf("Internal filter: errType=%v\n", errType) fmt.Println("\n=== Error Handling Examples Completed ===") diff --git a/examples/indirect/main.go b/examples/indirect/main.go index 8d50891..b429338 100644 --- a/examples/indirect/main.go +++ b/examples/indirect/main.go @@ -355,7 +355,9 @@ func (app *App) createBuffers() error { mappedSlice := unsafe.Slice((*Vertex)(ptr), len(vertices)) copy(mappedSlice, vertices) } - app.vertexBuffer.Unmap() + if err := app.vertexBuffer.Unmap(); err != nil { + log.Printf("unmap vertex buffer: %v", err) + } // Create indirect buffer with draw arguments // This is the key part - the GPU reads these parameters! @@ -388,7 +390,9 @@ func (app *App) createBuffers() error { if indirectPtr != nil { *(*wgpu.DrawIndirectArgs)(indirectPtr) = indirectArgs } - app.indirectBuffer.Unmap() + if err := app.indirectBuffer.Unmap(); err != nil { + log.Printf("unmap indirect buffer: %v", err) + } return nil } diff --git a/examples/instanced/main.go b/examples/instanced/main.go index 834518b..3c9c8e7 100644 --- a/examples/instanced/main.go +++ b/examples/instanced/main.go @@ -145,7 +145,9 @@ func main() { mappedSlice := unsafe.Slice((*Vertex)(ptr), len(triangleVertices)) copy(mappedSlice, triangleVertices) } - vertexBuffer.Unmap() + if unmapErr := vertexBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap vertex buffer: %v", unmapErr) + } // Create instance buffer (per-instance data) instanceBufferSize := uint64(len(instanceData)) * uint64(unsafe.Sizeof(instanceData[0])) @@ -165,7 +167,9 @@ func main() { mappedSlice := unsafe.Slice((*InstanceData)(ptr), len(instanceData)) copy(mappedSlice, instanceData) } - instanceBuffer.Unmap() + if unmapErr := instanceBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap instance buffer: %v", unmapErr) + } // Define vertex attributes for per-vertex buffer vertexAttributes := []wgpu.VertexAttribute{ diff --git a/examples/mrt/main.go b/examples/mrt/main.go index 0da150d..b9f13b5 100644 --- a/examples/mrt/main.go +++ b/examples/mrt/main.go @@ -419,7 +419,9 @@ func (app *App) createVertexBuffer() error { copy(mappedSlice, vertices) // Unmap buffer to commit data to GPU - app.vertexBuffer.Unmap() + if unmapErr := app.vertexBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap vertex buffer: %v", unmapErr) + } return nil } diff --git a/examples/rotating-triangle/main.go b/examples/rotating-triangle/main.go index 27055fc..1ad2478 100644 --- a/examples/rotating-triangle/main.go +++ b/examples/rotating-triangle/main.go @@ -371,7 +371,9 @@ func (app *App) createVertexBuffer() error { copy(mappedSlice, vertices) // Unmap buffer to commit data to GPU - app.vertexBuffer.Unmap() + if err := app.vertexBuffer.Unmap(); err != nil { + log.Printf("unmap: %v", err) + } return nil } diff --git a/examples/textured-quad/main.go b/examples/textured-quad/main.go index 4541be1..cfea102 100644 --- a/examples/textured-quad/main.go +++ b/examples/textured-quad/main.go @@ -507,7 +507,9 @@ func (app *App) createBuffers() error { // nolint:gosec,govet // vPtr is from GetMappedRange, validated non-nil, safe for slice conversion vMappedSlice := unsafe.Slice((*float32)(vPtr), len(vertices)) copy(vMappedSlice, vertices) - app.vertexBuffer.Unmap() + if unmapErr := app.vertexBuffer.Unmap(); unmapErr != nil { + log.Printf("unmap vertex buffer: %v", unmapErr) + } // Create index buffer // nolint:gosec // len(indices) is 6, * 2 = 12 bytes - no overflow risk @@ -530,7 +532,9 @@ func (app *App) createBuffers() error { // nolint:gosec,govet // iPtr is from GetMappedRange, validated non-nil, safe for slice conversion iMappedSlice := unsafe.Slice((*uint16)(iPtr), len(indices)) copy(iMappedSlice, indices) - app.indexBuffer.Unmap() + if err := app.indexBuffer.Unmap(); err != nil { + log.Printf("unmap index buffer: %v", err) + } return nil } diff --git a/examples/timestamp_query/main.go b/examples/timestamp_query/main.go index 01ab87c..ef89a8b 100644 --- a/examples/timestamp_query/main.go +++ b/examples/timestamp_query/main.go @@ -218,7 +218,9 @@ fn main(@builtin(global_invocation_id) global_id: vec3) { startTimestamp := *(*uint64)(unsafe.Pointer(&data[0])) endTimestamp := *(*uint64)(unsafe.Pointer(&data[8])) - stagingBuffer.Unmap() + if err := stagingBuffer.Unmap(); err != nil { + log.Printf("unmap staging buffer: %v", err) + } // Calculate elapsed ticks. // Note: To convert to nanoseconds, you need the timestamp period diff --git a/examples/triangle/main.go b/examples/triangle/main.go index 14cc5af..c525a3b 100644 --- a/examples/triangle/main.go +++ b/examples/triangle/main.go @@ -401,8 +401,8 @@ func (app *App) render() error { defer encoder.Release() // Render triangle - if err := app.renderTriangle(encoder, app.surfaceTexView); err != nil { - return err + if renderErr := app.renderTriangle(encoder, app.surfaceTexView); renderErr != nil { + return renderErr } // Finish encoding diff --git a/go.mod b/go.mod index 2d9faa8..343a367 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,8 @@ module github.com/go-webgpu/webgpu go 1.25.0 -require github.com/go-webgpu/goffi v0.5.2 +require github.com/go-webgpu/goffi v0.6.0 -require golang.org/x/sys v0.42.0 +require golang.org/x/sys v0.47.0 -require github.com/gogpu/gputypes v0.5.0 +require github.com/gogpu/gputypes v0.5.1 diff --git a/go.sum b/go.sum index 8579796..050135c 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,6 @@ -github.com/go-webgpu/goffi v0.5.2 h1:Kq2llPlA6IhkkmAffkM5CtsgaXuBQC4mBI0BOREwV04= -github.com/go-webgpu/goffi v0.5.2/go.mod h1:wfoxNsJkU+5RFbV1kNN1kunhc1lFHuJKK3zpgx08/uM= -github.com/gogpu/gputypes v0.5.0 h1:i2ED/9w6m6yLxf8XJT69/NIMSNTLO2y5F1LqvugCKIE= -github.com/gogpu/gputypes v0.5.0/go.mod h1:cnXrDMwTpWTvJLW1Vreop3PcT6a2YP/i3s91rPaOavw= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +github.com/go-webgpu/goffi v0.6.0 h1:dTBwfzj8CZUW0w0fgeMaYGBrIktK7nzfjMsnSpkSt4Y= +github.com/go-webgpu/goffi v0.6.0/go.mod h1:wfoxNsJkU+5RFbV1kNN1kunhc1lFHuJKK3zpgx08/uM= +github.com/gogpu/gputypes v0.5.1 h1:X38OPcP6umQqqubzzJYL6Nm1tXHSNQj6TRSAoxdAJmg= +github.com/gogpu/gputypes v0.5.1/go.mod h1:cnXrDMwTpWTvJLW1Vreop3PcT6a2YP/i3s91rPaOavw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= diff --git a/internal/nativelib/download.go b/internal/nativelib/download.go index 48b7fc8..c0ffc45 100644 --- a/internal/nativelib/download.go +++ b/internal/nativelib/download.go @@ -51,12 +51,12 @@ func Download(url string) (string, error) { if _, err := io.Copy(tmpFile, io.LimitReader(resp.Body, maxLibSize)); err != nil { tmpFile.Close() //nolint:errcheck // cleanup on write error - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return "", fmt.Errorf("download write: %w", err) } if err := tmpFile.Close(); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return "", fmt.Errorf("close %s: %w", tmpPath, err) } diff --git a/internal/nativelib/platform.go b/internal/nativelib/platform.go index 9d6358c..6f30801 100644 --- a/internal/nativelib/platform.go +++ b/internal/nativelib/platform.go @@ -78,7 +78,7 @@ func LibraryName() string { func FindLibrary() string { if p := os.Getenv("WGPU_NATIVE_PATH"); p != "" { - if _, err := os.Stat(p); err == nil { + if _, err := os.Stat(p); err == nil { //nolint:gosec // G703: user-provided env var path, validated by Stat return p } } @@ -102,7 +102,7 @@ func FindLibrary() string { for _, dir := range strings.Split(os.Getenv("PATH"), string(os.PathListSeparator)) { p := filepath.Join(dir, libName) - if _, err := os.Stat(p); err == nil { + if _, err := os.Stat(p); err == nil { //nolint:gosec // G703: dir from PATH env, libName from internal constants return p } } diff --git a/setup/setup.go b/setup/setup.go index b2edafc..3311f07 100644 --- a/setup/setup.go +++ b/setup/setup.go @@ -32,8 +32,8 @@ func Install(destDir string) (string, error) { if destDir == "" { destDir = "lib" } - if err := os.MkdirAll(destDir, 0o755); err != nil { - return "", fmt.Errorf("create directory %s: %w", destDir, err) + if mkdirErr := os.MkdirAll(destDir, 0o755); mkdirErr != nil { //nolint:gosec // G301: 0755 is standard for library directories + return "", fmt.Errorf("create directory %s: %w", destDir, mkdirErr) } url := platform.DownloadURL(Version) @@ -44,7 +44,7 @@ func Install(destDir string) (string, error) { if err != nil { return "", err } - defer os.Remove(zipPath) + defer func() { _ = os.Remove(zipPath) }() fmt.Printf("Extracting %s...\n", platform.LibName) libPath, err := nativelib.ExtractLibrary(zipPath, destDir, platform.LibName) diff --git a/wgpu/loader_unix.go b/wgpu/loader_unix.go index 01e5eb8..3ddd56d 100644 --- a/wgpu/loader_unix.go +++ b/wgpu/loader_unix.go @@ -116,7 +116,7 @@ func (u *unixProc) Call(args ...uintptr) (uintptr, uintptr, error) { // Call the function var result uintptr - err := ffi.CallFunction(&u.cif, u.fnPtr, unsafe.Pointer(&result), argPtrs) + _, err := ffi.CallFunction(&u.cif, u.fnPtr, unsafe.Pointer(&result), argPtrs) if err != nil { return 0, 0, fmt.Errorf("wgpu: call to %s failed: %w", u.name, err) }