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
34 changes: 28 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.9.1] - 2026-01-05

### Fixed

#### Vulkan Backend
- **vkDestroyDevice Memory Leak** — Fixed memory leak when destroying Vulkan devices ([#32])
- Device was not properly destroyed due to missing goffi call
- Now correctly calls `vkDestroyDevice` via `ffi.CallFunction` with `SigVoidHandlePtr` signature
- **Features Mapping** — Implemented `featuresFromPhysicalDevice()` ([#33])
- Maps 9 Vulkan features to WebGPU features (BC, ETC2, ASTC, IndirectFirstInstance, etc.)
- Reference: wgpu-hal/src/vulkan/adapter.rs:584-829
- **Limits Mapping** — Implemented proper Vulkan→WebGPU limits mapping ([#34])
- Maps 25+ hardware limits from `VkPhysicalDeviceLimits`
- Includes: texture dimensions, descriptor limits, buffer limits, compute limits
- Reference: wgpu-hal/src/vulkan/adapter.rs:1254-1392

[#32]: https://github.com/gogpu/wgpu/issues/32
[#33]: https://github.com/gogpu/wgpu/issues/33
[#34]: https://github.com/gogpu/wgpu/issues/34

## [0.9.0] - 2026-01-05

### Added
Expand All @@ -30,11 +50,11 @@ The following features are not yet fully implemented in the Vulkan backend:

| Feature | Status | Target |
|---------|--------|--------|
| Feature Detection | Returns 0 (all disabled) | v0.5.0 |
| Limits Mapping | Uses conservative defaults | v0.5.0 |
| Array Textures | Single layer only | v0.6.0 |
| Render Bundles | Not implemented | v0.6.0 |
| Timestamp Period | Hardcoded to 1.0 | v0.6.0 |
| Feature Detection | ~~Returns 0~~ **Fixed in v0.9.1** | ✅ |
| Limits Mapping | ~~Uses defaults~~ **Fixed in v0.9.1** | ✅ |
| Array Textures | Single layer only | v0.10.0 |
| Render Bundles | Not implemented | v0.10.0 |
| Timestamp Period | Hardcoded to 1.0 | v0.10.0 |

**Note:** Basic rendering (triangles, textures, compute) works correctly. These limitations affect capability reporting and advanced features only.

Expand Down Expand Up @@ -355,7 +375,9 @@ The following features are not yet fully implemented in the Vulkan backend:
- **Noop backend** (`hal/noop/`) - Reference implementation for testing
- **OpenGL ES backend** (`hal/gles/`) - Pure Go via goffi (~3.5K LOC)

[Unreleased]: https://github.com/gogpu/wgpu/compare/v0.8.8...HEAD
[Unreleased]: https://github.com/gogpu/wgpu/compare/v0.9.1...HEAD
[0.9.1]: https://github.com/gogpu/wgpu/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/gogpu/wgpu/compare/v0.8.8...v0.9.0
[0.8.8]: https://github.com/gogpu/wgpu/compare/v0.8.7...v0.8.8
[0.8.7]: https://github.com/gogpu/wgpu/compare/v0.8.6...v0.8.7
[0.8.6]: https://github.com/gogpu/wgpu/compare/v0.8.5...v0.8.6
Expand Down
21 changes: 18 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

---

## Current Status: v0.9.0
## Current Status: v0.9.1

| Component | Status | LOC | Coverage |
|-----------|--------|-----|----------|
Expand Down Expand Up @@ -117,7 +117,7 @@
- [x] Skip Metal tests on CI (Metal unavailable in virtualized macOS)
- [x] Updated naga v0.8.2 → v0.8.3 (MSL `[[position]]` attribute fix)

### v0.9.0 — Core-HAL Bridge (Current)
### v0.9.0 — Core-HAL Bridge
- [x] **Snatchable Pattern** — Safe deferred resource destruction
- [x] **TrackerIndex Allocator** — Dense index allocation for state tracking
- [x] **Buffer State Tracker** — Buffer usage state validation
Expand All @@ -126,11 +126,26 @@
- [x] **Core CommandEncoder** — Command recording with HAL dispatch
- [x] **Code Quality** — 58 TODO comments replaced with proper documentation

### v0.9.1 — Vulkan Backend Fixes (Current)
- [x] **vkDestroyDevice Fix** — Fixed memory leak when destroying Vulkan devices ([#32])
- Device was not properly destroyed due to missing goffi call
- Now correctly invokes `vkDestroyDevice` via `ffi.CallFunction`
- [x] **Features Mapping** — Implemented `featuresFromPhysicalDevice()` ([#33])
- Maps 9 Vulkan features to WebGPU features
- BC, ETC2, ASTC compression, IndirectFirstInstance, MultiDrawIndirect, etc.
- [x] **Limits Mapping** — Implemented proper Vulkan→WebGPU limits ([#34])
- Maps 25+ hardware limits from `VkPhysicalDeviceLimits`
- Texture dimensions, descriptor limits, buffer limits, compute limits

[#32]: https://github.com/gogpu/wgpu/issues/32
[#33]: https://github.com/gogpu/wgpu/issues/33
[#34]: https://github.com/gogpu/wgpu/issues/34

---

## Upcoming Releases

### v0.9.0 — Compute Shaders
### v0.10.0 — Compute Shaders
**Target: Q1 2025**

- [ ] Compute pipeline support in all backends
Expand Down
16 changes: 10 additions & 6 deletions hal/vulkan/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"unsafe"

"github.com/go-webgpu/goffi/ffi"
"github.com/gogpu/wgpu/hal"
"github.com/gogpu/wgpu/hal/vulkan/vk"
"github.com/gogpu/wgpu/types"
Expand Down Expand Up @@ -189,14 +190,17 @@ func vkGetDeviceQueue(cmds *vk.Commands, device vk.Device, queueFamilyIndex, que
cmds.GetDeviceQueue(device, queueFamilyIndex, queueIndex, queue)
}

func vkDestroyDevice(device vk.Device, _ *vk.AllocationCallbacks) {
// Note: This requires device commands to be loaded, but we're destroying the device
// For now, use GetDeviceProcAddr directly
func vkDestroyDevice(device vk.Device, allocator *vk.AllocationCallbacks) {
// Get vkDestroyDevice function pointer directly since device commands
// may not be available when destroying the device
proc := vk.GetDeviceProcAddr(device, "vkDestroyDevice")
if proc == nil {
return
}
// FIXME(v0.4.1): vkDestroyDevice requires goffi signature update. Minor memory leak.
// For now just skip - device will be destroyed when process exits
_ = proc
// Call vkDestroyDevice(VkDevice, VkAllocationCallbacks*) via goffi
args := [2]unsafe.Pointer{
unsafe.Pointer(&device),
unsafe.Pointer(&allocator),
}
_ = ffi.CallFunction(&vk.SigVoidHandlePtr, proc, nil, args[:])
}
95 changes: 92 additions & 3 deletions hal/vulkan/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (i *Instance) EnumerateAdapters(surfaceHint hal.Surface) []hal.ExposedAdapt
vkVersionPatch(props.ApiVersion)),
Backend: types.BackendVulkan,
},
Features: 0, // Note(v0.5.0): Feature mapping pending. See DX12 adapter.go:204 for pattern.
Features: featuresFromPhysicalDevice(&features),
Capabilities: hal.Capabilities{
Limits: limitsFromProps(&props),
AlignmentsMask: hal.Alignments{
Expand Down Expand Up @@ -336,7 +336,96 @@ func vendorIDToName(id uint32) string {
}
}

// featuresFromPhysicalDevice maps Vulkan physical device features to WebGPU features.
// Reference: wgpu-hal/src/vulkan/adapter.rs:584-829
func featuresFromPhysicalDevice(features *vk.PhysicalDeviceFeatures) types.Features {
var result types.Features

// Texture compression features
if features.TextureCompressionBC != 0 {
result |= types.Features(types.FeatureTextureCompressionBC)
}
if features.TextureCompressionETC2 != 0 {
result |= types.Features(types.FeatureTextureCompressionETC2)
}
if features.TextureCompressionASTC_LDR != 0 {
result |= types.Features(types.FeatureTextureCompressionASTC)
}

// Draw features
if features.DrawIndirectFirstInstance != 0 {
result |= types.Features(types.FeatureIndirectFirstInstance)
}
if features.MultiDrawIndirect != 0 {
result |= types.Features(types.FeatureMultiDrawIndirect)
}

// Depth/clipping features
if features.DepthClamp != 0 {
result |= types.Features(types.FeatureDepthClipControl)
}

// Shader features
if features.ShaderFloat64 != 0 {
result |= types.Features(types.FeatureShaderFloat64)
}

// Query features
if features.PipelineStatisticsQuery != 0 {
result |= types.Features(types.FeaturePipelineStatisticsQuery)
}

// Depth32FloatStencil8 is always available in Vulkan 1.0+
result |= types.Features(types.FeatureDepth32FloatStencil8)

return result
}

// limitsFromProps maps Vulkan physical device limits to WebGPU limits.
// Reference: wgpu-hal/src/vulkan/adapter.rs:1254-1392
func limitsFromProps(props *vk.PhysicalDeviceProperties) types.Limits {
_ = props // Note(v0.5.0): Limits mapping pending. DefaultLimits() is safe. See DX12 adapter.go:242.
return types.DefaultLimits()
vkLimits := props.Limits

// Start with default limits and override with actual hardware values
limits := types.DefaultLimits()

// Texture dimensions
limits.MaxTextureDimension1D = vkLimits.MaxImageDimension1D
limits.MaxTextureDimension2D = vkLimits.MaxImageDimension2D
limits.MaxTextureDimension3D = vkLimits.MaxImageDimension3D
limits.MaxTextureArrayLayers = vkLimits.MaxImageArrayLayers

// Descriptor/binding limits
limits.MaxBindGroups = min(vkLimits.MaxBoundDescriptorSets, 8) // WebGPU max is 8
limits.MaxSampledTexturesPerShaderStage = vkLimits.MaxPerStageDescriptorSampledImages
limits.MaxSamplersPerShaderStage = vkLimits.MaxPerStageDescriptorSamplers
limits.MaxStorageBuffersPerShaderStage = vkLimits.MaxPerStageDescriptorStorageBuffers
limits.MaxStorageTexturesPerShaderStage = vkLimits.MaxPerStageDescriptorStorageImages
limits.MaxUniformBuffersPerShaderStage = vkLimits.MaxPerStageDescriptorUniformBuffers

// Buffer limits
limits.MaxUniformBufferBindingSize = uint64(vkLimits.MaxUniformBufferRange)
limits.MaxStorageBufferBindingSize = uint64(vkLimits.MaxStorageBufferRange)
limits.MinUniformBufferOffsetAlignment = uint32(vkLimits.MinUniformBufferOffsetAlignment)
limits.MinStorageBufferOffsetAlignment = uint32(vkLimits.MinStorageBufferOffsetAlignment)

// Vertex limits
limits.MaxVertexAttributes = min(vkLimits.MaxVertexInputAttributes, 32) // WebGPU max is 32
limits.MaxVertexBufferArrayStride = min(vkLimits.MaxVertexInputBindingStride, 2048)

// Color attachment limits
limits.MaxColorAttachments = min(vkLimits.MaxColorAttachments, 8) // WebGPU max is 8

// Compute limits
limits.MaxComputeWorkgroupStorageSize = vkLimits.MaxComputeSharedMemorySize
limits.MaxComputeInvocationsPerWorkgroup = vkLimits.MaxComputeWorkGroupInvocations
limits.MaxComputeWorkgroupSizeX = vkLimits.MaxComputeWorkGroupSize[0]
limits.MaxComputeWorkgroupSizeY = vkLimits.MaxComputeWorkGroupSize[1]
limits.MaxComputeWorkgroupSizeZ = vkLimits.MaxComputeWorkGroupSize[2]
limits.MaxComputeWorkgroupsPerDimension = vkLimits.MaxComputeWorkGroupCount[0]

// Push constants
limits.MaxPushConstantSize = vkLimits.MaxPushConstantsSize

return limits
}
Loading
Loading