Skip to content

fix(gles): Instance-owned GL context on hidden window (Rust wgpu parity)#191

Merged
kolkov merged 2 commits into
mainfrom
feat/gles-hidden-window
May 21, 2026
Merged

fix(gles): Instance-owned GL context on hidden window (Rust wgpu parity)#191
kolkov merged 2 commits into
mainfrom
feat/gles-hidden-window

Conversation

@kolkov
Copy link
Copy Markdown
Contributor

@kolkov kolkov commented May 21, 2026

Summary

  • Move GL context ownership from Surface to Instance using a hidden 1×1 HWND
  • Previously Surface owned the GL context — closing the window killed it, leaving Adapter/Device/Queue with dangling references
  • New AdapterContext with sync.Mutex + runtime.LockOSThread() + lazy GL init via sync.Once
  • Surface becomes lightweight (no context ownership), enabling multi-window support and renderer lifecycle decoupling
  • Follows Rust wgpu-hal/src/gles/wgl.rs AdapterContext::lock()/lock_with_dc() pattern

Architecture

Component Before After
GL context owner Surface Instance (hidden 1×1 HWND)
Context creation CreateSurface Lazy in first Lock() on render thread
Thread safety None sync.Mutex + runtime.LockOSThread
Surface.Configure Single lock scope (double unmake bug) Two lock scopes: swap interval + FBO
Unlock guard None wglGetCurrentContext() check (Rust pattern)
Surface destruction Kills GL context Context survives (on hidden window)

New files (3)

  • wgl/hidden_window.go — hidden HWND, CS_OWNDC window class, lifecycle
  • adapter_context.go — mutex-protected GL context with lazy init
  • surface_linux_compat.go — compat wrappers for Linux (Phase 2)

Test plan

  • Build: Windows, Linux, macOS, WASM
  • Tests: 15/15 pass
  • Lint: 0 issues on all 3 platforms
  • Standalone MakeCurrent lifecycle test (cmd/gles-debug)
  • Visual: GOGPU_GRAPHICS_API=gles triangle renders on Intel Iris Xe, OpenGL 4.6
  • CI: GitHub Actions (Linux Mesa Surfaceless for GLES integration test)

kolkov added 2 commits May 21, 2026 19:20
Move GL context from Surface to Instance using a hidden 1×1 HWND.
Previously Surface owned the GL context — closing the window killed it,
leaving Adapter/Device/Queue with dangling references.

Architecture (follows Rust wgpu-hal/src/gles/wgl.rs):
- Instance creates hidden 1×1 window with CS_OWNDC + pixel format at init
- GL context lazily created on first Lock() via sync.Once on render thread
  (avoids cross-thread WGL issues — Go goroutines migrate between OS threads)
- AdapterContext: sync.Mutex + runtime.LockOSThread pins goroutine during GL
- Lock() → wglMakeCurrent(hiddenDC, hglrc) for resource creation and submit
- LockForDC(userDC) → wglMakeCurrent(userDC, hglrc) for presentation
- Unlock() → wglMakeCurrent(0,0) with wglGetCurrentContext guard (Rust pattern)
- Surface.Configure: two lock scopes (swap interval on user DC, FBO on hidden DC)
- Surface lightweight: only HWND + shared *AdapterContext reference

New files:
- wgl/hidden_window.go: hidden HWND, window class, lifecycle management
- adapter_context.go: mutex-protected GL context with lazy init
- surface_linux_compat.go: compat wrappers for Linux (Phase 2 pending)

Verified: triangle rendering on Intel Iris Xe, OpenGL 4.6.
Build: Windows/Linux/macOS/WASM. Tests: 15/15. Lint: 0 all platforms.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@kolkov kolkov merged commit 673358a into main May 21, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant