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

## [Unreleased]

## [0.9.3] - 2026-01-10

Critical Intel Vulkan fixes: VkRenderPass support, wgpu-style swapchain synchronization.

### Added

#### HAL
- **ErrDriverBug Error** — New error type for driver specification violations
- Returned when GPU driver violates API spec (e.g., returns success but invalid handle)
- Provides actionable guidance: update driver, try different backend, or use software rendering

#### Vulkan Backend
- **VkRenderPass Support** — Classic render pass implementation for Intel compatibility
- New `renderpass.go` with VkRenderPass and VkFramebuffer management
- Switched from VK_KHR_dynamic_rendering (broken on Intel) to classic approach
- Works across all GPU vendors
- **wgpu-Style Swapchain Synchronization** — Proper frame pacing for Windows/Intel
- Rotating acquire semaphores (one per max frames in flight)
- Per-image present semaphores
- Post-acquire fence wait (fixes "Not Responding" on Windows)
- Per-acquire fence tracking for stutter-free rendering
- **Fence Status Optimization** — Skip unnecessary fence waits
- `vkGetFenceStatus` check before blocking wait
- Improves frame latency when GPU is already done
- **Device Management** — New methods for resource management
- `Device.WaitIdle()` — Wait for all GPU operations
- `Device.ResetCommandPool()` — Reset all command buffers
- **WSI Function Loading** — Explicit loading of Window System Integration functions

### Fixed

#### Vulkan Backend
- **Intel Null Pipeline Workaround** — Defensive check for Intel Vulkan driver bug
- Intel Iris Xe drivers may return `VK_SUCCESS` but write `VK_NULL_HANDLE` to pipeline
- Returns `hal.ErrDriverBug` instead of crashing
- **goffi Pointer Argument Passing** — Fixed FFI calling convention
- goffi expects pointer-to-pointer pattern for pointer arguments
- **vkGetDeviceProcAddr Loading** — Fixed device function loading on Intel
- **Validation Layer Availability** — Gracefully skip validation if Vulkan SDK not installed

### Changed
- Updated naga dependency v0.8.3 → v0.8.4 (SPIR-V instruction ordering fix)

### Dependencies
- `github.com/gogpu/naga` v0.8.4 (was v0.8.3)

## [0.9.2] - 2026-01-05

### Fixed
Expand Down
6 changes: 5 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

---

## Current Status: v0.9.2
## Current Status: v0.9.3

| Component | Status | LOC | Coverage |
|-----------|--------|-----|----------|
Expand Down Expand Up @@ -215,6 +215,10 @@ Priority areas:

| Version | Date | Highlights |
|---------|------|------------|
| v0.9.3 | 2026-01 | Intel Vulkan fix: VkRenderPass, wgpu-style sync |
| v0.9.2 | 2026-01 | Metal NSString double-free fix |
| v0.9.1 | 2026-01 | Dependencies update |
| v0.9.0 | 2026-01 | Major Vulkan improvements |
| v0.8.4 | 2025-12 | Naga v0.8.1 (clamp() fix) |
| v0.8.3 | 2025-12 | Metal macOS blank window fix |
| v0.8.2 | 2025-12 | Naga v0.8.0 (HLSL backend) |
Expand Down
119 changes: 119 additions & 0 deletions cmd/vulkan-renderpass-test/ffi_direct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2025 The GoGPU Authors
// SPDX-License-Identifier: MIT

//go:build windows

//nolint:gosec // Low-level FFI diagnostic tool requires unsafe operations
package main

import (
"fmt"
"syscall"
"unsafe"

"github.com/go-webgpu/goffi/ffi"
"github.com/go-webgpu/goffi/types"
"github.com/gogpu/wgpu/hal/vulkan/vk"
)

// DirectCreatePipeline tests direct FFI call to vkCreateGraphicsPipelines
// bypassing our wrapper to isolate if the issue is in the wrapper or goffi.
func DirectCreatePipeline(cmds *vk.Commands, device vk.Device, createInfo *vk.GraphicsPipelineCreateInfo) (vk.Pipeline, vk.Result, error) {
// Get the function pointer
fnPtr := cmds.DebugFunctionPointer("vkCreateGraphicsPipelines")
if fnPtr == nil {
return 0, 0, fmt.Errorf("vkCreateGraphicsPipelines not loaded")
}

// Prepare call interface manually
var cif types.CallInterface
resultRet := types.SInt32TypeDescriptor
u64 := types.UInt64TypeDescriptor
u32 := types.UInt32TypeDescriptor
ptr := types.PointerTypeDescriptor

err := ffi.PrepareCallInterface(&cif, types.DefaultCall, resultRet,
[]*types.TypeDescriptor{u64, u64, u32, ptr, ptr, ptr})
if err != nil {
return 0, 0, fmt.Errorf("PrepareCallInterface failed: %w", err)
}

// Prepare arguments
pipelineCache := vk.PipelineCache(0)
createInfoCount := uint32(1)
var allocator unsafe.Pointer // nil
var pipeline vk.Pipeline

// For goffi: args[i] must be pointer to WHERE the value is stored
createInfoPtr := unsafe.Pointer(createInfo)
allocatorPtr := allocator
pipelinePtr := unsafe.Pointer(&pipeline)

args := [6]unsafe.Pointer{
unsafe.Pointer(&device),
unsafe.Pointer(&pipelineCache),
unsafe.Pointer(&createInfoCount),
unsafe.Pointer(&createInfoPtr),
unsafe.Pointer(&allocatorPtr),
unsafe.Pointer(&pipelinePtr),
}

var result int32
fmt.Printf(" Direct FFI call:\n")
fmt.Printf(" fnPtr: %p\n", fnPtr)
fmt.Printf(" device: 0x%X\n", device)
fmt.Printf(" createInfoPtr: %p\n", createInfoPtr)
fmt.Printf(" pipelinePtr (output): %p, value: 0x%X\n", pipelinePtr, pipeline)

_ = ffi.CallFunction(&cif, fnPtr, unsafe.Pointer(&result), args[:])

fmt.Printf(" result: %d\n", result)
fmt.Printf(" pipeline after call: 0x%X\n", pipeline)

return pipeline, vk.Result(result), nil
}

// SyscallCreatePipeline tests using Windows syscall directly
// to completely bypass goffi and test if it's a goffi issue.
func SyscallCreatePipeline(cmds *vk.Commands, device vk.Device, createInfo *vk.GraphicsPipelineCreateInfo) (vk.Pipeline, vk.Result, error) {
// Get the function pointer
fnPtr := cmds.DebugFunctionPointer("vkCreateGraphicsPipelines")
if fnPtr == nil {
return 0, 0, fmt.Errorf("vkCreateGraphicsPipelines not loaded")
}

// Prepare arguments for syscall6
// VkResult vkCreateGraphicsPipelines(
// VkDevice device,
// VkPipelineCache pipelineCache,
// uint32_t createInfoCount,
// const VkGraphicsPipelineCreateInfo* pCreateInfos,
// const VkAllocationCallbacks* pAllocator,
// VkPipeline* pPipelines
// )

var pipeline vk.Pipeline

fmt.Println(" Syscall6 call:")
fmt.Printf(" fnPtr: %p\n", fnPtr)
fmt.Printf(" device: 0x%X\n", device)
fmt.Printf(" createInfo: %p\n", createInfo)
fmt.Printf(" pipeline addr: %p\n", &pipeline)

// Call via syscall6
r1, _, _ := syscall.SyscallN(
uintptr(fnPtr),
uintptr(device),
0, // pipelineCache = VK_NULL_HANDLE
1, // createInfoCount = 1
uintptr(unsafe.Pointer(createInfo)),
0, // pAllocator = NULL
uintptr(unsafe.Pointer(&pipeline)),
)

result := vk.Result(int32(r1))
fmt.Printf(" result: %d\n", result)
fmt.Printf(" pipeline: 0x%X\n", pipeline)

return pipeline, result, nil
}
Loading
Loading