Series: Building Go's GPU Ecosystem
- GoGPU: A Pure Go Graphics Library — Project announcement
- From Idea to 100K Lines in Two Weeks — The journey
- Pure Go 2D Graphics with GPU Acceleration — Introducing gg
- GPU Compute Shaders in Pure Go — Compute pipelines
- Go 1.26 Meets 2026 — Roadmap
- Enterprise 2D Graphics Library — gg architecture
- Cross-Package GPU Integration — gpucontext
- Unified 2D/3D Graphics Integration ← You are here
Introduction
Today we announce a major milestone for the GoGPU ecosystem: unified 2D/3D graphics integration through standardized interfaces. This release enables seamless rendering of 2D graphics (via gg) into GPU-accelerated windows (via gogpu) — all in Pure Go, without CGO.
This is the foundation for gogpu/ui, our upcoming enterprise-grade GUI toolkit.
The Problem We Solved
Modern applications need both 2D and 3D graphics:
- UI elements (text, buttons, icons) require 2D rendering
- Visualization (charts, graphs, CAD) requires GPU acceleration
- Games and simulations require both
Traditionally, integrating these required:
- CGO bindings to native libraries (Cairo, Skia, Qt)
- Complex texture management between CPU and GPU
- Tight coupling between graphics and windowing code
We solved this with interface-based architecture — a pattern proven in enterprise systems.
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ User Application │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────────────┐ │
│ │ gg (2D Graphics)│ │ gogpu (GPU Framework) │ │
│ │ │ │ │ │
│ │ - Canvas API │ │ - WebGPU abstraction │ │
│ │ - Text/Fonts │ │ - Multi-backend │ │
│ │ - Paths/Shapes │ │ - Window management │ │
│ └────────┬────────┘ └────────────┬─────────────┘ │
│ │ │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ gpucontext │ │
│ │ (Shared Interfaces)│ │
│ │ │ │
│ │ - TextureDrawer │ │
│ │ - TextureCreator │ │
│ │ - DeviceProvider │ │
│ │ - EventSource │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
The key insight: shared interfaces enable integration without coupling.
The TextureDrawer Interface
At the heart of our integration is gpucontext.TextureDrawer:
// gpucontext/texture.go
// TextureDrawer provides texture drawing capabilities for 2D rendering.
// This interface enables packages like ggcanvas to draw textures without
// depending directly on gogpu, following the Dependency Inversion Principle.
type TextureDrawer interface {
// DrawTexture draws a texture at the specified position.
DrawTexture(tex Texture, x, y float32) error
// TextureCreator returns the texture creator associated with this drawer.
TextureCreator() TextureCreator
}
// TextureCreator provides texture creation from raw pixel data.
type TextureCreator interface {
// NewTextureFromRGBA creates a texture from RGBA pixel data.
NewTextureFromRGBA(width, height int, data []byte) (Texture, error)
}
This interface follows the Dependency Inversion Principle:
-
ggdepends on abstractions (gpucontext.TextureDrawer) -
gogpuimplements abstractions (Context.AsTextureDrawer()) - Neither depends on the other
Working Example
Here's a simplified example based on our codebase (see full version at examples/gg_integration/main.go):
package main
import (
"log"
"math"
"github.com/gogpu/gg"
"github.com/gogpu/gg/integration/ggcanvas"
"github.com/gogpu/gogpu"
"github.com/gogpu/gogpu/gmath"
)
func main() {
// Create GPU-accelerated window
app := gogpu.NewApp(gogpu.DefaultConfig().
WithTitle("GoGPU + gg Integration via ggcanvas").
WithSize(800, 600))
var canvas *ggcanvas.Canvas
app.OnDraw(func(dc *gogpu.Context) {
w, h := dc.Width(), dc.Height()
dc.ClearColor(gmath.Hex(0x1a1a2e))
// Lazy initialization with GPU context
if canvas == nil {
var err error
canvas, err = ggcanvas.New(app.GPUContextProvider(), w, h)
if err != nil {
log.Fatalf("Failed to create canvas: %v", err)
}
}
// Draw 2D graphics using familiar gg API
cc := canvas.Context()
cc.SetRGB(1, 0.5, 0)
cc.DrawCircle(400, 300, 100)
cc.Fill()
// Render to GPU window — one line!
canvas.RenderTo(dc.AsTextureDrawer())
})
// Handle window resize
app.EventSource().OnResize(func(w, h int) {
if canvas != nil {
canvas.Resize(w, h)
}
})
app.Run()
}
Key points:
-
ggcanvas.New(provider, w, h)— Creates a canvas with GPU context -
canvas.Context()— Returns standard*gg.Contextfor 2D drawing -
canvas.RenderTo(dc.AsTextureDrawer())— Uploads to GPU and draws
Note: The full example includes animated HSV-colored circles, debug PNG export, backend logging, and comprehensive error handling.
The ggcanvas package handles all complexity:
- CPU→GPU texture upload
- Dirty tracking (only upload when changed)
- Format conversion (RGBA→GPU texture)
- Resource cleanup
Under the Hood
Data Flow
User draws via gg API
│
▼
gg.Context accumulates draw commands
│
▼
canvas.RenderTo(dc) called
│
├─── cc.RenderToPixmap(pixmap) [CPU rasterization]
│
├─── texture.UpdateData(pixmap.Pix) [CPU→GPU upload]
│
└─── dc.DrawTexture(texture, 0, 0) [GPU render]
│
▼
Window surface (Vulkan/Metal/DX12)
gogpu Implementation
gogpu.Context implements the interface via an adapter:
// context_texture.go
// AsTextureDrawer returns an adapter implementing gpucontext.TextureDrawer.
func (c *Context) AsTextureDrawer() gpucontext.TextureDrawer {
return &contextTextureDrawer{
ctx: c,
creator: &rendererTextureCreator{renderer: c.renderer},
}
}
This follows the Adapter Pattern — exposing existing functionality through a new interface without modifying the original type.
Platform Support
This integration works across all gogpu-supported platforms:
| Platform | Backend | Status |
|---|---|---|
| Windows | Vulkan, DX12 | Production |
| Linux (X11) | Vulkan | Production |
| Linux (Wayland) | Vulkan | Production |
| macOS | Metal | Production |
All platforms use Pure Go FFI — no CGO required.
Performance Characteristics
| Operation | Cost | Notes |
|---|---|---|
canvas.Context() |
O(1) | Returns existing context |
| 2D drawing | CPU | Rasterization in gg |
RenderTo() |
O(pixels) | CPU→GPU texture upload |
| GPU draw | O(1) | Single textured quad |
For static or infrequently changing UI, the CPU→GPU upload happens only when content changes (dirty tracking).
Roadmap: gogpu/ui
This integration is the foundation for gogpu/ui, our upcoming GUI toolkit:
// Future gogpu/ui API (planned)
func main() {
app := gogpu.NewApp(gogpu.DefaultConfig().
WithTitle("gogpu/ui Demo").
WithSize(1280, 720))
// Declarative UI with reactive state
count := signals.New(0)
root := layout.VStack(
widgets.Text("Counter Demo").FontSize(24),
layout.HStack(
widgets.Button("-").OnClick(func() {
count.Set(count.Get() - 1)
}),
widgets.Text(signals.Computed(func() string {
return fmt.Sprintf("Count: %d", count.Get())
})),
widgets.Button("+").OnClick(func() {
count.Set(count.Get() + 1)
}),
).Gap(8),
).Padding(24).Gap(16)
ui := ui.NewRenderer(app.GPUContextProvider())
ui.SetRoot(root)
app.OnDraw(func(dc *gogpu.Context) {
dc.ClearColor(theme.Background)
ui.RenderTo(dc.AsTextureDrawer())
})
// Event forwarding
app.EventSource().OnMouse(ui.HandleMouse)
app.EventSource().OnKeyboard(ui.HandleKeyboard)
app.EventSource().OnTouch(ui.HandleTouch)
app.Run()
}
Planned Features
| Phase | Version | Features |
|---|---|---|
| Phase 1 | v0.1.0 | Core widgets, Flexbox layout, Events |
| Phase 2 | v0.2.0 | Material 3 theme, Animation |
| Phase 3 | v0.3.0 | Virtualization (100K+ items), A11y |
| Phase 4 | v1.0.0 | IDE docking, Multiple themes |
Design Decisions
Based on our research of 7 Rust UI frameworks:
| Decision | Choice | Rationale |
|---|---|---|
| Reactivity | Fine-grained signals | O(affected) updates only |
| Styling | Tailwind-style builders | Type-safe, IDE autocomplete |
| Layout | Flexbox + incremental | Industry standard |
| Accessibility | AccessKit schema | Cross-platform standard |
Today's Release
| Package | Version | Highlights |
|---|---|---|
| gpucontext | v0.4.0 |
TextureDrawer, TouchEventSource
|
| wgpu | v0.12.0 | BufferRowLength fix |
| naga | v0.9.0 | Shader compiler improvements |
| gg | v0.22.1 | Integration + LineJoinRound fix |
| gogpu | v0.14.0 |
AsTextureDrawer() implementation |
Getting Started
go get github.com/gogpu/gogpu@v0.14.0
go get github.com/gogpu/gg@v0.22.1
Run the example:
git clone https://github.com/gogpu/gogpu
cd gogpu
go run examples/gg_integration/main.go
Resources
| Resource | Link |
|---|---|
| GitHub Organization | github.com/gogpu |
| RFC Discussion | gogpu/ui RFC |
| gpucontext Article | Cross-Package GPU Integration |
| gg Architecture | Enterprise 2D Graphics Library |
| Project History | From Idea to 100K Lines |
We Need Your Help
Building an enterprise-grade graphics ecosystem is a massive undertaking. We're a small team, and we need the community's help:
Testing & Bug Reports
- Platform testing — macOS, Linux (X11/Wayland), different GPUs
- Edge cases — unusual window sizes, high DPI, multi-monitor setups
- Performance issues — stuttering, memory leaks, high CPU usage
Validation & Feedback
- API review — Does the API feel Go-idiomatic? What's confusing?
- Architecture validation — Are we making the right design decisions?
- Real-world usage — Try it in your projects and report pain points
What We're Especially Looking For
| Area | What We Need |
|---|---|
| Rendering bugs | Incorrect colors, missing pixels, artifacts |
| Performance bottlenecks | Profile and identify slow paths |
| API ergonomics | Confusing names, missing methods, rough edges |
| Platform issues | Windows/macOS/Linux-specific problems |
| Integration feedback | How well does it fit your use case? |
How to Contribute
- Star the repos — Helps visibility
- File issues — Even small bugs matter
- Join discussions — github.com/orgs/gogpu/discussions
- Try the examples — Report what breaks
Every bug report, performance profile, and piece of feedback helps us build the graphics library Go deserves.
Conclusion
With unified 2D/3D integration, the GoGPU ecosystem is ready for the next step: a production-grade GUI toolkit for Go.
Our approach:
- Pure Go — No CGO, easy cross-compilation
- Interface-based — Clean architecture, testable
- Enterprise patterns — Proven in large-scale systems
We're building the GUI toolkit Go deserves. Join the discussion at github.com/orgs/gogpu/discussions/18.
The GoGPU Team
Follow the project:
- GitHub: github.com/gogpu
- Twitter: @gogpu_go
Top comments (0)