Releases: gogpu/wgpu
v0.29.14
What's Changed
Fix: Missing vertexBuffers in Linux RenderPipeline
One-line fix that enables full gg rendering on GLES Linux — SDF shapes, text, widgets all working.
device_linux.go CreateRenderPipeline did not store desc.Vertex.Buffers in the pipeline struct. The Windows device.go had it. Without vertex buffer layout, vertex attributes were never configured → all geometry silently discarded → blank window.
gg rendering on GLES Linux Wayland (WSL2, Mesa d3d12 gallium, GL 4.1) — SDF shapes, text, widgets at ~57 FPS
Found by @lkmavi (PR #215). Thank you Vladimir!
Full Changelog: v0.29.13...v0.29.14
v0.29.13
What's Changed
Enterprise GLES Parity for Linux
Major release for GLES backend on Linux — 18 files, +1159/-58. First Pure Go GLES rendering on Linux Wayland (triangle verified on WSL2 Mesa d3d12 GL 4.1).
First Pure Go GLES rendering on Linux Wayland (WSL2, Mesa d3d12 gallium, GL 4.1 Core Profile)
GLSL version propagation (Rust wgpu-hal parity): compileWGSLToGLSL no longer hardcodes #version 430. The detected GL_SHADING_LANGUAGE_VERSION flows from adapter → device → naga GLSL writer. On GL 4.1 emits #version 410 core.
Runtime binding fallback for GL < 4.2 (Rust device.rs:438-461): When layout(binding=N) is unavailable (GLSL < 420), bindings are assigned after glLinkProgram via glGetUniformBlockIndex + glUniformBlockBinding (UBOs) and glGetUniformLocation + glUniform1i (samplers).
MSAA validation: CreateTexture validates sample count against GL_MAX_SAMPLES and checks glGetError after texture allocation — returns proper error instead of leaving stale GL errors in queue.
GLES-aware GL function loading: gl.Context.Load() accepts isGLES flag. On GLES contexts, loads glClearDepthf (not glClearDepth), tries OES suffix for VAO functions.
Lazy VAO creation: Persistent VAO allocated in CreateCommandEncoder (after Configure), not during Adapter.Open (before Configure).
Compute dispatch barrier: VERTEX_ATTRIB_ARRAY_BARRIER_BIT added for SSBO→vertex buffer reads (particles). Found by @lkmavi.
Wayland EGL fixes: WindowBit-only tier (Mesa no PbufferBit), skip instance context, GLES 3.0 fallback, CoreProfile guard for GLES. Credits: @lkmavi (PR #210, #215).
Dependencies
- naga v0.17.14 —
SupportsExplicitLocations(),UniformInforeflection, version-gatedlayout(binding=N)emission
Known Issues
- gg rendering (SDF shapes, text) not yet working on GL 4.1 — Wayland EGL alpha channel + gg-specific rendering pipeline. Separate gg task.
Full Changelog: v0.29.12...v0.29.13
v0.29.12
What's Changed
Fix: Wayland EGL nativeDisplay=0 fallback to surfaceless
GetEGLDisplay with nativeDisplay=0 on Wayland tried eglGetPlatformDisplay(WAYLAND, 0) which opens a second wl_display connection via wl_display_connect(NULL). When CreateSurface later used the app's real wl_surface* (on the app's display) with EGL's separate display → "Proxy and queue point to different wl_displays" crash.
Fix: Skip Wayland platform when nativeDisplay=0 (Instance init), fall back to Mesa surfaceless. CreateSurface calls again with the real wl_display*. Also: CreateSurface Path A skips sharing Instance context when it used surfaceless display (can't eglCreateWindowSurface on surfaceless).
Validated against Rust wgpu-hal egl.rs:905-968 platform detection chain. Found by @lkmavi.
Full Changelog: v0.29.11...v0.29.12
v0.29.11
What's Changed
GLES Enterprise Parity — Shared Context + Tiered Config + CI GL Test
Completes ADR-045 (items 4-6 that v0.29.10 missed):
1. Shared context in CreateSurface: When Instance has a pre-created EGL context (X11/headless), CreateSurface shares it (ownsContext=false) instead of creating a new one. On Wayland (no Instance context), creates its own (ownsContext=true). Matches Windows AdapterContext pattern where Surface is lightweight.
2. Tiered EGL config selection (Rust wgpu-hal egl.rs:218-293): chooseEGLConfig now tries WINDOW+PBUFFER first, falls back to PBUFFER-only. Ensures the config supports both headless rendering and window presentation.
3. CI GL object creation test: TestGLObjectCreation verifies GenBuffers, GenTextures, GenVertexArrays, GenFramebuffers return non-zero IDs through goffi on Mesa surfaceless. Catches the FFI pointer convention bug found by @lkmavi (PR #210, ADR-044).
4. ROADMAP updated to v0.29.11 — all v0.29.x releases documented.
Full Changelog: v0.29.10...v0.29.11
v0.29.10
What's Changed
Instance-level EGL context on Linux (Rust wgpu-hal parity)
CreateInstance now creates an EGL context via surfaceless/pbuffer at init time, matching Rust wgpu-hal egl.rs:846 and the Windows hidden window pattern (v0.28.6). EnumerateAdapters returns real GPU name, vendor, and driver version instead of placeholder "Unknown" values.
On Wayland (no wl_display* at init), gracefully degrades — CreateSurface provides the context later. @lkmavi's nil guard (PR #210) protects the edge case.
EGL surfaceless context fallback
NewContext checks for EGL_KHR_surfaceless_context or EGL 1.5+ before creating a pbuffer surface (Rust wgpu-hal egl.rs:735-758 parity). When surfaceless is supported, eglMakeCurrent(EGL_NO_SURFACE) works without a dummy 1×1 pbuffer — saves a surface allocation on modern Mesa/NVIDIA/ANGLE drivers.
FFI pointer convention fix (PR #210, @lkmavi)
30+ GL calls in context_linux.go fixed: pointer-type arguments now correctly use unsafe.Pointer(&pointer) instead of unsafe.Pointer(&value) for goffi CallFunction. This was a fundamental bug that prevented GLES rendering on Linux entirely. See ADR-044 for the full convention documentation. Thank you Vladimir (@lkmavi) for this critical contribution!
Full Changelog: v0.29.9...v0.29.10
v0.29.9
What's Changed
Fix Wayland SHM triple-buffer freeze (gogpu#292)
Two bugs caused presentation to freeze after 3 frames on Wayland software backend:
1. bufferBusyMap pointer mismatch: waylandCreateShmBuffer stored a pointer to a temporary heap struct in bufferBusyMap. The caller then did *buf = *newBuf (value copy into wl.buffers[idx]), creating two separate memory locations. The release callback set busy=false on the dead temporary, not on the array element that waylandPresent checks. After 3 commits (one per buffer), all buffers were permanently busy=true.
Fix: register in bufferBusyMap AFTER the value copy, with pointer to &wl.buffers[idx].
2. dispatch_queue_pending empty queue: wl_display_dispatch_queue_pending only processes events already read from the socket. But nobody was reading wire data into shmQueue on the render thread — gogpu's read_events runs independently on the main thread.
Fix: use wl_display_roundtrip_queue(display, shmQueue) which does the full prepare_read → read_events → dispatch cycle.
Verified
Particles example (4096 compute-driven particles) renders continuously for 10+ seconds on WSL2 Wayland. Triple-buffer cycles correctly with release callbacks.
Full Changelog: v0.29.8...v0.29.9
v0.29.8
What's Changed
Wayland display wrapper pattern — queue mismatch fix (gogpu#292)
obtainWlShm() created wl_registry and wl_shm proxies via raw wl_display*, placing them on the DEFAULT queue. Since gogpu dispatches only its own appQueue (ADR-041), events for DEFAULT queue objects were never delivered.
Fix: uses the wl_proxy_create_wrapper pattern (Qt6 qwaylandwindow.cpp:1799-1802) so all software backend proxies (registry, wl_shm, pool, buffer) inherit shmQueue:
wrapper = wl_proxy_create_wrapper(display)
wl_proxy_set_queue(wrapper, shmQueue)
registry = wl_display_get_registry(wrapper) → inherits shmQueue
wl_shm = wl_registry_bind(registry) → inherits shmQueue
pool/buffer → inherit from wl_shm
Uses wl_display_roundtrip_queue instead of wl_display_roundtrip to dispatch the correct queue.
Also fixes two proxy leaks: wl_registry and wl_shm are now properly destroyed in destroyWaylandBlitState(). Removes manual wl_proxy_set_queue on buffer proxies — queue inheritance is automatic via libwayland proxy_create (wayland-client.c:492: proxy->queue = factory->queue).
Validated against enterprise references: Qt6 (ephemeral wrapper), SDL3 (long-lived wrapper), winit (registry_queue_init). Confirmed on WSL2 via debug traces.
Full Changelog: v0.29.7...v0.29.8
v0.29.7
What's Changed
Fix Wayland SIGSEGV on first present (gogpu#292)
obtainWlShm() called wl_display_roundtrip() on the render thread during first present, concurrent with the main thread's Wayland event dispatch. Two concurrent dispatches on the same wl_display corrupt proxy state → SIGSEGV at offset 0x18 (wl_proxy.display field from NULL).
Fix: moved Wayland detection + wl_shm binding from lazy init (first blit on render thread) to eager init in Surface.Configure() (main thread, before event loop). This matches all enterprise Wayland implementations:
- GLFW:
glfwInit()—wl_init.c:568, fatal error ifwl_shmmissing - SDL3:
SDL_Init(SDL_INIT_VIDEO)—SDL_waylandvideo.c:1548 - Qt6:
QWaylandDisplay()—qwaylanddisplay.cpp:531, explicit comment: "We have to wait until we have an eventDispatcher before creating the eventThread, otherwise forceRoundTrip() may block" - winit:
EventLoop::new()—event_loop/mod.rs:92
Affects both reporters from gogpu#292: @lkmavi (ARM64 Fedora) and @omer316 (x86_64 Pop!_OS).
Files changed
hal/software/blit_linux.go— addconfigurePlatformBlit()with eager Wayland init; remove lazy detection fromblitFramebufferToWindowandblitDamageRectsToWindowhal/software/resource.go— callconfigurePlatformBlit()fromConfigure()hal/software/blit_windows.go,blit_darwin.go,blit_other.go— no-op stubs
Full Changelog: v0.29.6...v0.29.7
v0.29.6
What's Changed
Fence.Signal returns error on glFenceSync failure (Rust parity)
Fence.Signal() silently fell back to marking submissions as immediately complete when glFenceSync returned 0 (GPU out of memory). Now returns error, propagated through Queue.Submit(). Matches Rust wgpu-hal fence.rs:37-52 which returns Result<(), DeviceError::OutOfMemory>.
Follow-up to v0.29.5 fence ordering fix — found during method-by-method comparison of our fence lifecycle against Rust wgpu-hal.
Files changed
hal/gles/resource.go—Fence.Signalsignature:Signal(value uint64)→Signal(value uint64) errorhal/gles/queue.go— propagate Signal error in Submit (Windows)hal/gles/queue_linux.go— propagate Signal error in Submit (Linux)
Full Changelog: v0.29.5...v0.29.6
v0.29.5
What's Changed
Bug fix: GLES fence sync ordering (Windows + Linux)
Fixed glFenceSync being called after glFlush in Queue.Submit(). The fence sync object must be inserted into the GL command stream before the flush — otherwise the fence sits in an unflushed client-side buffer and PollCompleted (which uses non-blocking glGetSynciv) never sees it signal.
Impact: deferred resource destruction could stall in headless or compute-only workloads where no SwapBuffers provides an implicit flush. Headed rendering was unaffected because SwapBuffers masks the bug.
Verified against:
- Rust wgpu-hal
queue.rs:1915-1921— identical fence→flush ordering with explicit comment - ANGLE (Chromium) bug 6464 — Samsung discovered the same class of bug in production
- Virglrenderer commit
21bbc9ea— same fix for Windows/macOS GL stalls - Mesa Gallium state tracker — internally flushes during
glFenceSync, explaining why the bug was masked on Linux Mesa drivers
Optimized PollCompleted
Removed redundant Maintain() call from PollCompleted(). Rust wgpu-hal only calls get_latest() in the poll path (device.rs:1564); fence cleanup happens during Submit(). This avoids double GetLatest() calls per poll.
Files changed
hal/gles/queue.go— fence ordering + PollCompleted (Windows)hal/gles/queue_linux.go— fence ordering + PollCompleted (Linux)hal/gles/resource.go— updatedFence.Signaldoc comment
Full Changelog: v0.29.4...v0.29.5