Pure Go GPU Computing Ecosystem
GPU power, Go simplicity. Zero CGO.
GoGPU is a GPU computing framework for Go that provides a high-level API for graphics and compute operations. It supports dual backends: a high-performance Rust backend (wgpu-native) and a pure Go backend for zero-dependency builds.
| Category | Capabilities |
|---|---|
| Backends | Rust (wgpu-native) or Pure Go (gogpu/wgpu) |
| Graphics API | Runtime selection: Vulkan, DX12, Metal, GLES, Software |
| Platforms | Windows (Vulkan/DX12/GLES), Linux X11/Wayland (Vulkan/GLES), macOS (Metal) |
| Rendering | Event-driven three-state model (idle/animating/continuous), zero-copy surface rendering |
| Graphics | Windowing, input handling, texture loading, frameless windows |
| Compute | Full compute shader support |
| Window Chrome | Frameless windows with custom title bars, DWM shadow, hit-test regions |
| HiDPI | Per-monitor DPI, WM_DPICHANGED, logical/physical coordinate split |
| Integration | DeviceProvider, WindowProvider, PlatformProvider, WindowChrome, SurfaceView |
| Logging | Structured logging via log/slog, silent by default |
| Build | Zero CGO with Pure Go backend |
go get github.com/gogpu/gogpuRequirements:
- Go 1.25+
CGO_ENABLED=0(Pure Go FFI requires CGO disabled)
Zero dependencies — just works:
CGO_ENABLED=0 go run .Note: On macOS and some Linux distros, CGO is enabled by default. Always set
CGO_ENABLED=0when building GoGPU projects.
package main
import (
"github.com/gogpu/gogpu"
"github.com/gogpu/gogpu/gmath"
)
func main() {
app := gogpu.NewApp(gogpu.DefaultConfig().
WithTitle("Hello GoGPU").
WithSize(800, 600))
app.OnDraw(func(dc *gogpu.Context) {
dc.DrawTriangleColor(gmath.DarkGray)
})
app.Run()
}Result: A window with a rendered triangle in approximately 20 lines of code, compared to 480+ lines of raw WebGPU.
GoGPU supports two WebGPU implementations, selectable at compile time or runtime.
# Pure Go backend (default, zero dependencies)
go build ./...
# Enable Rust backend (requires wgpu-native shared library)
go build -tags rust ./...// Auto-select best available (default)
app := gogpu.NewApp(gogpu.DefaultConfig())
// Explicit Rust backend
app := gogpu.NewApp(gogpu.DefaultConfig().WithBackend(gogpu.BackendRust))
// Explicit Pure Go backend
app := gogpu.NewApp(gogpu.DefaultConfig().WithBackend(gogpu.BackendGo))| Backend | Build Tag | Library | Use Case |
|---|---|---|---|
| Native Go | (default) | gogpu/wgpu | Zero dependencies, simple deployment |
| Rust | -tags rust |
wgpu-native via FFI | Maximum performance (all platforms) |
Note: Rust backend requires wgpu-native DLL.
Backend (Rust/Native) and Graphics API (Vulkan/DX12/Metal/GLES) are independent choices:
// Force Vulkan on Windows (instead of auto-detected default)
app := gogpu.NewApp(gogpu.DefaultConfig().
WithGraphicsAPI(gogpu.GraphicsAPIVulkan))
// Force DirectX 12 on Windows
app := gogpu.NewApp(gogpu.DefaultConfig().
WithGraphicsAPI(gogpu.GraphicsAPIDX12))
// Force GLES (useful for testing or compatibility)
app := gogpu.NewApp(gogpu.DefaultConfig().
WithGraphicsAPI(gogpu.GraphicsAPIGLES))
// Software backend — no GPU required, always available
// Windows: renders to screen via GDI. Linux/macOS: headless.
app := gogpu.NewApp(gogpu.DefaultConfig().
WithGraphicsAPI(gogpu.GraphicsAPISoftware))| Graphics API | Platforms | Constant |
|---|---|---|
| Auto | All (default) | gogpu.GraphicsAPIAuto |
| Vulkan | Windows, Linux | gogpu.GraphicsAPIVulkan |
| DX12 | Windows | gogpu.GraphicsAPIDX12 |
| Metal | macOS | gogpu.GraphicsAPIMetal |
| GLES | Windows, Linux | gogpu.GraphicsAPIGLES |
| Software | All (no GPU needed) | gogpu.GraphicsAPISoftware |
GPU resources are automatically cleaned up on shutdown when registered with TrackResource:
canvas, _ := ggcanvas.New(provider, 800, 600)
app.TrackResource(canvas) // auto-closed on shutdown, no OnClose neededResources are closed in LIFO (reverse) order after GPU idle, before device destruction. The shutdown sequence is: WaitIdle → tracked resources → OnClose → Renderer.Destroy().
ggcanvas auto-registration: When created via a provider that implements ResourceTracker (like App), ggcanvas auto-registers — no TrackResource call needed.
GC safety net: Textures use runtime.AddCleanup as a fallback — if you forget Destroy(), the GC will eventually clean up GPU resources. This is a safety net, not a replacement for explicit cleanup.
// Load from file (PNG, JPEG)
tex, err := renderer.LoadTexture("sprite.png")
defer tex.Destroy()
// Create from Go image
img := image.NewRGBA(image.Rect(0, 0, 128, 128))
tex, err := renderer.NewTextureFromImage(img)
// With custom filtering options
opts := gogpu.TextureOptions{
MagFilter: gputypes.FilterModeNearest, // Crisp pixels
AddressModeU: gputypes.AddressModeRepeat, // Tiling
}
tex, err := renderer.LoadTextureWithOptions("tile.png", opts)GoGPU exposes GPU resources through the DeviceProvider interface for integration with external libraries:
type DeviceProvider interface {
Device() hal.Device // HAL GPU device (type-safe Go interface)
Queue() hal.Queue // HAL command queue
SurfaceFormat() gputypes.TextureFormat
}
// Usage
provider := app.DeviceProvider()
device := provider.Device() // hal.Device — 30+ methods with error returns
queue := provider.Queue() // hal.Queue — Submit, WriteBuffer, ReadBufferFor integration with external libraries like gogpu/gg, use the standard gpucontext interfaces:
import "github.com/gogpu/gpucontext"
// Get gpucontext.DeviceProvider for external libraries
provider := app.GPUContextProvider()
device := provider.Device() // gpucontext.Device interface
queue := provider.Queue() // gpucontext.Queue interface
format := provider.SurfaceFormat() // gpucontext.TextureFormat
// Get gpucontext.EventSource for UI frameworks
events := app.EventSource()
events.OnKeyPress(func(key gpucontext.Key, mods gpucontext.Modifiers) {
// Handle keyboard input
})
events.OnMousePress(func(button gpucontext.MouseButton, x, y float64) {
// Handle mouse click
})This enables enterprise-grade dependency injection between packages without circular imports.
For GPU accelerators that need low-level HAL access (compute shaders, buffer readback):
import "github.com/gogpu/gpucontext"
provider := app.GPUContextProvider()
// Type-assert to HalProvider for direct HAL access
if hp, ok := provider.(gpucontext.HalProvider); ok {
halDevice := hp.HalDevice() // hal.Device for compute pipelines
halQueue := hp.HalQueue() // hal.Queue for command submission
}Used by gogpu/gg GPU SDF accelerator for compute shader dispatch on shared device.
For direct GPU rendering without CPU readback:
app.OnDraw(func(dc *gogpu.Context) {
view := dc.SurfaceView() // Current frame's GPU texture view
// Pass to ggcanvas.RenderDirect() for zero-copy compositing
})This eliminates the GPU→CPU→GPU round-trip when integrating with gg/ggcanvas.
App implements gpucontext.WindowProvider and gpucontext.PlatformProvider for UI frameworks:
// Window geometry and DPI
w, h := app.Size() // logical points (DIP)
fw, fh := app.PhysicalSize() // physical pixels (framebuffer)
scale := app.ScaleFactor() // 1.0 = standard, 2.0 = Retina/HiDPI
// Clipboard
text, _ := app.ClipboardRead()
app.ClipboardWrite("copied text")
// Cursor management
app.SetCursor(gpucontext.CursorPointer) // hand cursor
app.SetCursor(gpucontext.CursorText) // I-beam for text input
// System preferences
if app.DarkMode() { /* switch to dark theme */ }
if app.ReduceMotion() { /* disable animations */ }
if app.HighContrast() { /* increase contrast */ }
fontMul := app.FontScale() // user's font size preferenceFor game loops, use the polling-based Input API:
import "github.com/gogpu/gogpu/input"
app.OnUpdate(func(dt float64) {
inp := app.Input()
// Keyboard
if inp.Keyboard().JustPressed(input.KeySpace) {
player.Jump()
}
if inp.Keyboard().Pressed(input.KeyLeft) {
player.MoveLeft(dt)
}
// Mouse
x, y := inp.Mouse().Position()
if inp.Mouse().JustPressed(input.MouseButtonLeft) {
player.Shoot(x, y)
}
})All input methods are thread-safe and work with the frame-based update loop.
GoGPU uses a three-state rendering model for optimal power efficiency:
| State | Condition | CPU Usage | Latency |
|---|---|---|---|
| Idle | No activity | 0% (blocks on OS events) | <1ms wakeup |
| Animating | Active animation tokens | VSync (~60fps) | Smooth |
| Continuous | ContinuousRender=true |
100% (game loop) | Immediate |
// Event-driven mode (default for UI apps)
app := gogpu.NewApp(gogpu.DefaultConfig().
WithContinuousRender(false))
// Start animation — renders at VSync while token is alive
token := app.StartAnimation()
// ... animation runs at 60fps ...
token.Stop() // Loop returns to idle (0% CPU)
// Request single-frame redraw from any goroutine
app.RequestRedraw()Multiple animation tokens can be active simultaneously. The loop renders continuously until all tokens are stopped.
Use OnClose to release GPU resources before the renderer is destroyed:
app.OnClose(func() {
if canvas != nil {
_ = canvas.Close()
canvas = nil
}
})
if err := app.Run(); err != nil {
log.Fatal(err)
}OnClose runs on the render thread before Renderer.Destroy(), ensuring textures, bind groups, and pipelines are released while the device is still alive.
Full compute shader support via HAL interfaces:
// Create compute pipeline via HAL device
pipeline, _ := device.CreateComputePipeline(&hal.ComputePipelineDescriptor{
Layout: pipelineLayout,
Module: shaderModule,
EntryPoint: "main",
})
// Create storage buffers
inputBuffer, _ := device.CreateBuffer(&hal.BufferDescriptor{
Size: dataSize,
Usage: gputypes.BufferUsageStorage | gputypes.BufferUsageCopyDst,
})
// Dispatch compute work via command encoder
encoder, _ := device.CreateCommandEncoder()
encoder.BeginEncoding("compute")
pass := encoder.BeginComputePass(&hal.ComputePassDescriptor{})
pass.SetPipeline(pipeline)
pass.SetBindGroup(0, bindGroup, nil)
pass.Dispatch(workgroupsX, 1, 1)
pass.End()
cmdBuf := encoder.EndEncoding()
queue.Submit([]hal.CommandBuffer{cmdBuf}, nil, 0)GoGPU uses log/slog for structured logging, silent by default:
import "log/slog"
// Enable info-level logging
gogpu.SetLogger(slog.Default())
// Enable debug-level logging for full diagnostics
gogpu.SetLogger(slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: slog.LevelDebug,
})))
// Get current logger
logger := gogpu.Logger()Log levels: Debug (texture creation, pipeline state), Info (backend selected, adapter info), Warn (resource cleanup errors).
GoGPU uses multi-thread architecture (Ebiten/Gio pattern) for professional responsiveness:
- Main thread: Window events only (Win32/Cocoa/X11 message pump)
- Render thread: All GPU operations (device, swapchain, commands)
This ensures windows never show "Not Responding" during heavy GPU operations.
User Application
│
▼
┌─────────────────────────────────────────────────────────┐
│ gogpu.App │
│ Multi-Thread: Events (main) + Render (dedicated) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ gogpu.Renderer │
│ Uses hal.Device / hal.Queue directly (Go interfaces) │
└─────────────────────────────────────────────────────────┘
│
├─────────────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ gogpu/wgpu │ │ Platform │
│ (Pure Go │ │ Windowing │
│ WebGPU) │ │ Win32/Cocoa │
└──────┬──────┘ └─────────────┘
│
┌─────┴─────┬─────┬─────┬─────────┐
▼ ▼ ▼ ▼ ▼
Vulkan DX12 Metal GLES Software
| Package | Purpose |
|---|---|
gogpu (root) |
App, Config, Context, Renderer, Texture |
gpu/ |
Backend selection (HAL-based) |
gpu/types/ |
BackendType, GraphicsAPI enums |
gpu/backend/rust/ |
Rust backend via wgpu-native FFI (opt-in, -tags rust) |
gpu/backend/native/ |
HAL backend creation (Vulkan/Metal selection) |
gmath/ |
Vec2, Vec3, Vec4, Mat4, Color |
window/ |
Window configuration |
input/ |
Keyboard and mouse input |
internal/platform/ |
Platform-specific windowing |
internal/thread/ |
Multi-thread rendering (RenderLoop) |
Native Win32 windowing with Vulkan, DirectX 12, GLES, and Software backends.
X11 and Wayland support with Vulkan, GLES, and Software (headless) backends.
- X11 — pure Go X11 protocol with libX11 loaded via goffi for Vulkan surface creation. Multi-touch input via XInput2 wire protocol.
- Wayland — pure Go Wayland protocol (object dispatch) with libwayland-client via goffi for Vulkan surfaces. Server-side decorations via
zxdg_decoration_manager_v1. Tested on WSLg, GNOME, KDE, sway.
Pure Go Cocoa implementation via goffi Objective-C runtime, with Metal and Software (headless) backends:
internal/platform/darwin/
├── application.go # NSApplication lifecycle
├── window.go # NSWindow, NSView management
├── surface.go # CAMetalLayer integration
└── objc.go # Objective-C runtime via goffi
Note: macOS Cocoa requires UI operations on the main thread. GoGPU handles this automatically.
| Project | Description |
|---|---|
| gogpu/gogpu | GPU framework (this repo) |
| gogpu/gpucontext | Shared interfaces (DeviceProvider, WindowProvider, PlatformProvider, EventSource) |
| gogpu/gputypes | Shared WebGPU types (TextureFormat, BufferUsage, Limits) |
| gogpu/wgpu | Pure Go WebGPU implementation |
| gogpu/naga | Shader compiler (WGSL to SPIR-V, MSL, GLSL) |
| gogpu/gg | 2D graphics library |
| gogpu/ui | GUI toolkit (planned) |
| go-webgpu/webgpu | wgpu-native FFI bindings |
| go-webgpu/goffi | Pure Go FFI library |
- ARCHITECTURE.md — System architecture
- ROADMAP.md — Development milestones
- CHANGELOG.md — Release notes
- pkg.go.dev — API reference
Contributions welcome! See GitHub Discussions to share ideas and ask questions.
Priority areas:
- Platform testing (macOS, Linux X11/Wayland, Windows DX12)
- Documentation and examples
- Performance benchmarks
- Bug reports
git clone https://github.com/gogpu/gogpu
cd gogpu
go build ./...
go test ./...Professor Ancha Baranova — This project would not have been possible without her invaluable help and support.
- u/m-unknown-2025 — The Reddit post that started it all
- born-ml/born — ML framework where go-webgpu bindings originated
| Contributor | Contributions |
|---|---|
| @ppoage | macOS ARM64 (Apple Silicon) support — 3 merged PRs across gogpu, wgpu, and naga with ~3,500 lines of code. Made Metal backend work on M1/M4 |
| @JanGordon | Documentation fix (wgpu) |
| Champion | Contributions |
|---|---|
| @darkliquid · Andrew Montgomery | Linux platform hero — 3 bug reports, 13+ comments with detailed stack traces and diagnostics. His persistence uncovered the critical goffi stack spill bug affecting all Linux/macOS users |
| @i2534 | Most prolific gg tester — 7 bug reports covering alpha blending, patterns, transforms, and line joins. Shaped the quality of the 2D renderer |
| @qq1792569310 · luomo | Early stress-tester — 3 issues and 9 comments. Found memory leak and event system bugs that improved framework stability |
| @rcarlier · Richard Carlier | Cross-platform tester — 4 issues across gg and ui. Active tester of text rendering, image handling, and UI on macOS Apple Silicon (M3) |
| @amortaza · Afshin Mortazavi-Nia | Architecture contributor — deep multi-week engagement in gg+gogpu integration discussions. Author of go-bellina UI library |
| @cyberbeast · Sandesh Gade | macOS Tahoe debugger — thorough Metal backend debugging on Apple M2 Max with detailed diagnostics |
| @crsolver | UI architecture advisor — significant input on the UI toolkit RFC with 8+ discussion comments |
| @neurlang | Wayland expert — author of neurlang/wayland, provided expert consultation on Wayland protocol issues |
These developers tested GoGPU on Day 1 — when nothing worked and every platform was broken. Their bug reports shaped the project:
- @Nickrocky — First macOS tester (Dec 25, 2025). The very first external user to try GoGPU
- @facemcgee — Early Linux tester (Dec 29, 2025)
- @soypat — Early naga interest, gsdf integration exploration
- @jan53n — Linux X11 testing
- @davidmichaelkarr — Windows 11 testing
- @martinarisk — Wayland testing, report that led to major protocol fixes
- @adamsanclemente — Found transform rendering bug in gg
- @beikege — Touch input advocacy, UI toolkit feedback
- @joeblew999 — WASM/browser platform interest
MIT License — see LICENSE for details.
GoGPU — Building the GPU computing ecosystem Go deserves