Skip to content

glplat: Ebitengine backend behind -tags glplat_ebiten (stacked on #392) - #744

Open
mparrett wants to merge 6 commits into
mainfrom
glplat-ebiten
Open

glplat: Ebitengine backend behind -tags glplat_ebiten (stacked on #392)#744
mparrett wants to merge 6 commits into
mainfrom
glplat-ebiten

Conversation

@mparrett

@mparrett mparrett commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #392. This branch carries #392's commits rebased onto current main (one .gitignore conflict, both sides kept) plus two of ours on top; only the tip two commits are for review here. It will be rebased onto whatever #392 lands as, and closed instead if the commits get folded into #392 directly. Draft until then.

A second registry.Backend implementation, on Ebitengine instead of GLFW/OpenGL. Two motivations: prove the backend seam with an independent implementation while #392 is still a draft and the interface is cheap to change, and get the two capabilities the GL backend cannot offer — the browser (ebiten's js/wasm target; the GL backend is cgo+GLFW, native only) and modern native APIs (Metal on macOS, where the GL backend rides deprecated OpenGL 4.1).

Ebiten owns the main thread, which inverts glplat's caller-owned frame loop. The backend absorbs the inversion so the API and game code see no difference: main routes through a boot trampoline (glplat.EbitenBootMain), the lg program runs on a goroutine, and EndFrame hands each frame's batches to ebiten's Update over an unbuffered channel, which doubles as frame pacing. MVP matrices are applied on the CPU since ebiten's DrawTriangles is 2D screen-space; rendering is at device scale to match the GL backend's framebuffer resolution.

Known limitation: ebiten has no depth buffer, so batches draw in submission order. That is exact for the top-down xsofy frontend (coplanar geometry under an ortho matrix) but mis-sorts first-person-mode geometry; a CPU back-to-front sort for perspective batches is the planned follow-up.

Verified (2026-08-14):

  • The unmodified xsofy GL frontend runs on this backend: XSOFY_GL_DUMP output byte-identical to the GL backend and deterministic across runs; screenshot pixel diff 0.3%.
  • CPU/memory parity over a 10s run; peak RSS flat from 30s to 60s (no leak), constant ~11MB above the GL backend.
  • GOOS=js GOARCH=wasm builds with the tag and a probe page running an lg draw loop renders correctly in Chromium — the blocking EndFrame contract survives wasm because the Go scheduler parks the blocked lg goroutine while ebiten's requestAnimationFrame loop runs.
  • Default (untagged) build, -tags glplat, and GOOS=linux/plan9/js builds are all unaffected.

🤖 Generated with Claude Code

nnunley and others added 6 commits August 14, 2026 10:38
…wport from framebuffer size each frame (Retina)
FontLoad (ttf/otf/ttc), FontHasGlyph, FontRasterizeCell (fit-to-cell alpha
grid), SaveGlyphAtlasPNG — minimal Go surface; layout/policy moved to
xsofy's tools/bake_atlas.lg. cmd/lgatlas deleted (replaced).
glReadPixels of the back buffer (call after rendering, before EndFrame
swaps), rows flipped, alpha forced opaque. Registry interface + native
backend + public API + rt binding.
Without the tag no backend registers: the pure-Go API returns
'no backend registered' errors, and plain go build ./..., headless CI,
and the GOOS=js wasm target no longer require GLFW/OpenGL/cgo.
Build with -tags glplat to get the native GLFW/OpenGL backend.
Second registry.Backend implementation, on Ebitengine instead of
GLFW/OpenGL. Ebiten owns the main thread (RunGame), so the root main
routes through a boot trampoline: the lg program runs on a goroutine and
EndFrame hands each frame's batches to Update over an unbuffered channel
— the rendezvous doubles as frame pacing. MVP is applied on the CPU
(ebiten is 2D screen-space); no depth buffer, so batches draw in
submission order — correct for the top-down frontend, mis-sorts 3D fp
geometry.

Conformance vs the GL backend on the unmodified xsofy gl-frontend
(2026-08-14): XSOFY_GL_DUMP output byte-identical, screenshot pixel diff
0.3% (glyph edges), CPU parity over a 10s run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Layout returned the logical window size, so on Retina the backend drew
at half the GL backend's framebuffer resolution and upscaled — visibly
rough glyphs. Render device-scaled and derive the NDC->screen mapping
from the actual target bounds (screenshots stay logical by design).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mparrett
mparrett marked this pull request as ready for review August 16, 2026 02:40
@mparrett
mparrett marked this pull request as draft August 16, 2026 06:02
@nooga

nooga commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Delta itself is clean (tag-gated deps confirmed absent from the default build, both build matrices pass, nnunley approved). Parked only because it's stacked on #392, which still has an open P1. Once #392's fixes land, rebase or fold these two commits in and this is a quick approve.

@mparrett
mparrett marked this pull request as ready for review September 2, 2026 02:44
@nnunley

nnunley commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Architecture dependency update (2026-09-07):

#744 remains blocked behind #392, but the intended direction is now explicit: use the merged #572 surface host-capability model, put headless font support behind a separate opt-in glplat_fonts tag, and define the backend-independent frame/window/input/lifecycle contract before landing either backend.

Please do not treat this as a request for a mechanical rebase yet. #392's unresolved safety/API findings still need fixes after the re-scope, and this Ebitengine backend should be evaluated against the resulting #572-based contract rather than the current glplat registry contract.

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.

3 participants