Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- name: Download wgpu-native
shell: bash
env:
WGPU_VERSION: "v27.0.4.0"
WGPU_VERSION: "v29.0.0.0"
run: |
set -e
case "${{ matrix.os }}" in
Expand Down Expand Up @@ -74,9 +74,9 @@ jobs:
shell: bash
run: |
if [ "${{ matrix.os }}" == "windows-latest" ]; then
go test -v ./wgpu/... -run "Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
go test -v ./wgpu/... -run "TestABI|Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
else
CGO_ENABLED=0 go test -v ./wgpu/... -run "Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
CGO_ENABLED=0 go test -v ./wgpu/... -run "TestABI|Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
fi

- name: Run fuzz tests (seed corpus only)
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Download wgpu-native
shell: bash
env:
WGPU_VERSION: "v27.0.4.0"
WGPU_VERSION: "v29.0.0.0"
run: |
set -e
case "${{ matrix.os }}" in
Expand Down Expand Up @@ -107,9 +107,9 @@ jobs:
shell: bash
run: |
if [ "${{ matrix.os }}" != "windows-latest" ]; then
CGO_ENABLED=0 go test -v -coverprofile=coverage.txt -covermode=atomic ./wgpu/... -run "Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
CGO_ENABLED=0 go test -v -coverprofile=coverage.txt -covermode=atomic ./wgpu/... -run "TestABI|Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
else
go test -v -race -coverprofile=coverage.txt -covermode=atomic ./wgpu/... -run "Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
go test -v -race -coverprofile=coverage.txt -covermode=atomic ./wgpu/... -run "TestABI|Mat4|Vec3|StructSizes|CheckInit|WGPUError|Fuzz|NullGuard"
fi

- name: Upload coverage to Codecov
Expand Down
60 changes: 60 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,66 @@ 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.0 (Unreleased)

### Breaking Changes
- **wgpu-native v29.0.0.0**: Migrated from v27.0.4.0 to v29.0.0.0 with stable webgpu-headers
- **API redesign**: All `Create*` methods now return `(*T, error)` instead of `*T`
- **Method renames**: `GetQueue()` → `Queue()`, `GetSize()` → `Size()`, `GetLimits()` → `Limits()`, etc.
- **Struct layout changes**: `Limits` field order fixed (ABI-breaking), `VertexBufferLayout` gains `nextInChain`
- **Removed types**: `SupportedLimits`, `ChainedStructOut`, `InstanceCapabilities`
- **Enum changes**: `SurfaceGetCurrentTextureStatus` simplified, `InstanceFlag_Default` semantic change
- **gputypes aliases**: Types re-exported for single-import ergonomics
- **Buffer.MapAsync** signature changed: `(mode, offset, size) (*MapPending, error)` — device argument removed, now stored in Buffer
- **Buffer.Unmap()** returns `error` (always nil per WebGPU spec; signature matches gogpu/wgpu)
- **Queue.Submit** returns `(uint64, error)` — submission index via `wgpuQueueSubmitForIndex` extension
- **Adapter.Limits()** returns `Limits` value (not pointer, no error) — cached at creation
- **Device.Limits()** returns `Limits` value (not pointer, no error) — cached at creation
- **BindGroupLayoutEntry** uses pointer sub-layouts (`*BufferBindingLayout`, `*SamplerBindingLayout`, etc.)
- **SamplerDescriptor.MaxAnisotropy** renamed to **Anisotropy**
- **Surface.Configure(device, config) error** — device is now a separate first argument
- **Surface.GetCurrentTexture()** returns `(*SurfaceTexture, bool, error)` — added suboptimal flag
- **Surface.Present(texture ...*SurfaceTexture) error** — takes optional texture argument

### Added
- 271 enterprise ABI verification tests (`TestABI*`)
- gputypes type aliases and constant re-exports in `wgpu` package
- New v29 API functions: `GetFeatures`, `GetInstanceFeatures`, `BufferReadMappedRange`, etc.
- New enums: `InstanceFeatureName`, `ComponentSwizzle`, `PredefinedColorSpace`, `ToneMappingMode`
- New instance flags: `GPUBasedValidation`, `Debugging`, `AdvancedDebugging`, `WithEnv`
- **`Buffer.Map(ctx, mode, offset, size) error`** — context-aware blocking mapping; drives Device.Poll internally
- **`Buffer.MapAsync(mode, offset, size) (*MapPending, error)`** — truly non-blocking, resolves on next Device.Poll
- **`Buffer.MappedRange(offset, size) (*MappedRange, error)`** — type-safe view over mapped memory
- **`MapPending`** type with `Status() (ready bool, err error)` and `Wait(ctx) error` methods
- **`MappedRange`** type with `Bytes() []byte`, `Len() int`, and `Offset() uint64` methods
- **`Queue.Submit`** returns submission index `uint64` via `wgpuQueueSubmitForIndex` wgpu-native extension
- **`ImageCopyTexture`** — Go-typed descriptor for texture copy/write source (holds `*Texture`)
- **`ImageDataLayout`** — Go-typed buffer layout descriptor for WriteTexture / copy operations
- **`BufferTextureCopy`** — region descriptor combining `ImageCopyTexture` + `ImageDataLayout` + extent
- **`TextureCopy`** — region descriptor for texture-to-texture copies
- **`CommandEncoder.CopyTextureToBuffer(src *Texture, dst *Buffer, regions []BufferTextureCopy)`** — region-based copy

### Changed
- `convert.go`: Removed 6 identity converters (TextureFormat now matches v29 natively)
- `wgpuAdapterEnumerateFeatures` → `wgpuAdapterGetFeatures` (single-call pattern)
- PushConstants → Immediates rename throughout
- `Buffer` now stores `*Device` reference internally for Poll-driven blocking Map
- All public descriptors use Go-idiomatic types: `string` labels, `bool` flags, pointer sub-structs, slice fields

### Removed
- `SupportedLimits` wrapper struct
- `ChainedStructOut` type (aliased to `ChainedStruct`)
- `InstanceCapabilities` struct
- DX11 backend support (`InstanceBackendDX11`)
- `SurfaceGetCurrentTextureStatusOutOfMemory`, `SurfaceGetCurrentTextureStatusDeviceLost`

### Dependencies
- wgpu-native: v27.0.4.0 → v29.0.0.0
- goffi: v0.5.0 (unchanged)
- gputypes: v0.3.0 (unchanged)

---

## [0.4.3] - 2026-03-29

### Changed
Expand Down
122 changes: 113 additions & 9 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ perf: optimize command encoder batch submission

- Go 1.25 or later
- golangci-lint v2
- wgpu-native shared libraries (download script provided)
- wgpu-native v29.0.0.0 shared libraries (download script provided)

### Platform Requirements

Expand Down Expand Up @@ -198,6 +198,20 @@ go test -v ./wgpu/... -run "TestDeviceCreation"
go test -bench=. -benchmem ./wgpu/...
```

Tests are organized into three tiers:

| Tier | Filter | GPU required | Runs in CI |
|------|--------|-------------|-----------|
| **ABI tests** | `-run "TestABI"` | No | Yes |
| **Safe tests** | `-run "Mat4\|Vec3\|StructSizes\|CheckInit\|WGPUError\|Fuzz\|NullGuard"` | No | Yes |
| **GPU tests** | `-run "TestAdapter\|TestDevice\|TestBuffer\|TestSurface\|TestLeak\|TestErrorScope"` | Yes | No |

**`abi_test.go`** contains 271 assertions that verify Go wire struct sizes and field offsets match the C ABI defined in wgpu-native v29 `webgpu.h`. These tests catch silent ABI regressions (wrong struct size, missing padding, field reorder) that would cause memory corruption at runtime. Run them after any struct change:

```bash
go test -v -run "TestABI" ./wgpu/...
```

### Running Linter

```bash
Expand Down Expand Up @@ -262,18 +276,102 @@ webgpu/
└── README.md # Main documentation
```

## Adding a New FFI Function

When wrapping a new wgpu-native function, follow this pattern exactly:

**Step 1: Add the procedure handle** (`wgpu/wgpu.go` or the relevant `*.go` file)

```go
var procDeviceNewFunction = loadProc("wgpuDeviceNewFunction")
```

**Step 2: Add the wire struct** (if the function takes a descriptor)

The wire struct must match the C struct layout from `webgpu.h` exactly:
```go
// wireNewFunctionDescriptor must match WGPUNewFunctionDescriptor in webgpu.h exactly.
type newFunctionDescriptorWire struct {
NextInChain uintptr // *WGPUChainedStruct — always 8 bytes
Label StringView // {Data uintptr, Length uintptr} — always 16 bytes
SomeField uint32 // matches C uint32_t
_ [4]byte // explicit padding to match C struct alignment
}
```

**Step 3: Add the public method** on the appropriate receiver type

```go
// NewFunction creates a new thing. Returns an error if the device is nil or
// wgpu-native reports a validation error.
func (d *Device) NewFunction(desc *NewFunctionDescriptor) (*NewFunction, error) {
if err := d.checkInit(); err != nil {
return nil, err
}
wire := newFunctionDescriptorWire{
Label: toStringView(desc.Label),
SomeField: toWGPUSomeEnum(desc.SomeField), // use converter if needed
}
handle := procDeviceNewFunction.callUintptr(d.handle, uintptr(unsafe.Pointer(&wire)))
if handle == 0 {
return nil, fmt.Errorf("wgpu: DeviceNewFunction failed")
}
f := &NewFunction{handle: handle}
trackResource(handle, "NewFunction")
return f, nil
}
```

**Step 4: Add Release method** on the new type

```go
// Release releases the GPU resource. Safe to call on a nil or already-released handle.
func (f *NewFunction) Release() {
if f == nil || f.handle == 0 {
return
}
untrackResource(f.handle)
procNewFunctionRelease.call(f.handle)
f.handle = 0
}
```

**Step 5: Update `abi_test.go`**

Add size and offset assertions for the new wire struct:
```go
{"newFunctionDescriptorWire", unsafe.Sizeof(newFunctionDescriptorWire{}), expectedSize},
```

Run `go test -run TestABI ./wgpu/...` to verify before pushing.

**Step 6: Add a test**

Even a basic null-guard test is required:
```go
func TestNullGuard_NewFunction(t *testing.T) {
var f *NewFunction
f.Release() // must not panic
}
```

See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the full explanation of the FFI layer design, wire structs, and the enum conversion rules.

---

## Adding New Features

1. Check if issue exists, if not create one
2. Discuss approach in the issue
3. Create feature branch from `develop`
4. Implement feature with tests
5. Update documentation and examples
6. Run quality checks (`bash scripts/pre-release-check.sh`)
7. Create pull request to `develop`
8. Wait for code review
9. Address feedback
10. Merge when approved
3. Create feature branch from `main`
4. Implement feature with tests (follow the FFI pattern above)
5. Update `abi_test.go` for any new wire structs
6. Update documentation and examples
7. Run quality checks (`bash scripts/pre-release-check.sh`)
8. Create pull request to `main`
9. Wait for code review
10. Address feedback
11. Merge when approved

## Code Style Guidelines

Expand Down Expand Up @@ -362,6 +460,12 @@ See [STABILITY.md](STABILITY.md) for the API stability policy. When deprecating
func OldFunction() { ... }
```

## Architecture Reference

For a deep dive into the internal design — wire structs vs public structs, the enum conversion layer, async callback pattern, and testing strategy — see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).

---

## Platform-Specific Notes

### Windows
Expand Down
Loading
Loading