Skip to content

iris-hostgl: an EGL backend, for host GL on Linux (stacked on #218) - #219

Closed
atomchild411 wants to merge 5 commits into
techomancer:mainfrom
atomchild411:linux-hostgl
Closed

atomchild411 wants to merge 5 commits into
techomancer:mainfrom
atomchild411:linux-hostgl

Conversation

@atomchild411

Copy link
Copy Markdown
Contributor

Stacked on #218 (its two commits come first here); review the last three. Once #218 is merged this shows only them.

--features hostgl gets a Linux backend: IRIX OpenGL and IRIS GL programs draw on a Linux host's GPU through EGL, as they do on a Mac through CGL.

What changes

  • iris-hostgl/src/backend/egl.rs: headless contexts in the compatibility profile with no surface at all (EGL_KHR_surfaceless_context); every drawable was already a framebuffer object, so no X or Wayland session is involved. The display is Mesa's surfaceless platform, else the first EGL device, else the default display. libEGL is dlopened; the GL entry points the crate calls by name come from libGL (glvnd), linked.
  • src/avail.rs and tools/gl_avail.py: glvnd and Mesa answer any gl* name with a do-nothing stub, so a missing entry point never comes back null, and the service needs a null to fall back from glFooSGI/glFooEXT to glFoo. The backend only looks a name up once the context's version or extensions provide it, from a table generated out of the pinned registry (tools/khronos/gl.xml).
  • Internal texture names count down from u32::MAX: Mesa's glGenTextures returns one more than the highest name in use, whatever the program bound by hand, and handed out names the service had taken for its own textures.
  • c_char for shader and string externs (u8 on arm64 Linux), Linux cfgs, register() picks the native backend.
  • Docs: FEATURES.md, iris-gui-README.md and iris-gui's build-info line no longer say macOS only; CHANGELOG; rules/hostgl/linux-egl-and-mesa.md; the registry README names gl_avail.py.

Building and running

hostgl stays opt-in and iris-hostgl outside the workspace, so a default build links nothing new. With --features hostgl on Linux:

build run
Debian / Ubuntu libgl-dev libegl1 libegl-mesa0 libgl1-mesa-dri (libegl-mesa0 alone does not bring libEGL)
Fedora libglvnd-devel mesa-libEGL mesa-dri-drivers
Arch libglvnd mesa

(Building iris at all on a clean system also needs libclang, for the camera's v4l2-sys-mit bindings; GitHub's runners have it already.) The guest side is unchanged: the same iris-guest-tools libraries.

Tested

Debian 12 arm64 (Parallels VM, Mesa 22.3, virgl, GL 4.0 compatibility), IP28 with Max IMPACT, IRIX 6.5.22, --features hostgl,jitv2,tcache,lightning,gpujit:

hostgl crate tests 31/31
glcheck n32 / o32 PASSED, reference checksum 0xa3d786cb
gltest n32 / o32 167/167, 167/167
irisgltest all ok
Quake 3 demo four, 1024x768 16.8 fps (4 vCPUs)

Fedora 44, Arch and Ubuntu 24.04, x86_64 containers with Mesa's software renderer and no GPU: the same features build, and the hostgl tests pass 31/31.

macOS unchanged: hostgl tests 31/31, iris-gui builds.

CI does not build iris-hostgl, so it says nothing about this PR.

🤖 Generated with Claude Code

atomchild411 and others added 5 commits October 12, 2026 20:37
A host call is answered on the thread that runs the emulated CPU, so while
host GL replayed a buffer the whole machine stood still: no instruction,
no interrupt. Big buffers took 2-5 ms; IRIX acknowledged its 1 kHz tick
late ~15 times a second, its audio driver refilled its few-millisecond DMA
ring late, and HAL2 played gaps -- 3% of a demo's sound, heard as crackle
on every loud low note.

The service now runs on a thread of its own (threaded.rs), which owns
every context. The CPU thread hands it work through a bounded queue:

- OP_BATCH_ASYNC (new): the buffer is read on the CPU thread (need-page as
  ever), queued and answered at once. The library sends only buffers that
  read nothing outside themselves this way; one that tries is cut short
  and logged. Without the thread it is OP_BATCH; a host without it answers
  EINVAL, which is how the library finds out -- the protocol number stays.
- Every other call runs on the GL thread after the buffers before it, and
  the CPU thread waits for its answer. It lends the GL thread its view of
  the program's memory for the call -- it does nothing else until the
  answer comes -- so a call costs two hand-offs however much it reads.
- But a call from a client with buffers still queued is answered ENOTYET
  (iris-hostcall: new, 0x3001) at once rather than held behind them: the
  library yields and asks again, so IRIX keeps running. Only a library that
  sends OP_BATCH_ASYNC can have buffers queued, so only one that knows
  ENOTYET is told it.

Each side spins ~30 us for its next message before it sleeps, since most
answers come sooner than a thread wakes. IRIS_HOSTGL_INLINE=1 keeps the
service on the CPU thread. The thread wants a host core of its own beside
the CPU thread.

Measured against this tree without the change, same binary features and
disk (IP28, Max IMPACT, macOS, the guest's libGL the async host-GL
library): a GL demo with synthesized sound went from 19.7 to 3.7 output
dropouts a second (31 -> 4 ms of sound lost a second; its score alone has
none either way) and Quake 3's timedemo from 30.2-30.5 to 33.9 fps. Not
host GL, unchanged: boot time, idle CPU, the score with no graphics, and
SGI's own libGL on the emulated board. gltest 167/167 n32 and o32,
glcheck passes with the reference checksum, irisgltest all ok. The new
library on a host without the thread falls back to OP_BATCH (6 of 6
gltest runs clean); libraries without OP_BATCH_ASYNC work unchanged.
gltest's "the window kept its pixels" fails now and then on either
build, with or without the new library.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Headless contexts in the compatibility profile through EGL, with no
surface at all (EGL_KHR_surfaceless_context): every drawable is already a
framebuffer object. The display is Mesa's surfaceless platform where
there is one, else the first EGL device, else the default. libEGL is
opened at run time; the entry points the crate calls by name come from
libGL (glvnd). A swap reads back with glReadPixels: there is no
CPU-addressable surface yet.

Looking entry points up by name needs a table. glvnd and Mesa answer any
gl* name with a callable stub, so a missing function never comes back
null, and Exec::func relies on null to fall back from an SGI or EXT name
to the core one. avail.rs, generated from the pinned registry by
tools/gl_avail.py, says which version or extension provides each name;
the backend looks a name up only when the context has it.

On Linux arm64 C's char is unsigned, so the shader entry points take
c_char, not i8.

Tested on Debian 12 arm64 in Parallels (virgl, OpenGL 4.0 compatibility,
Mesa 22.3.6): the crate's 27 tests pass, and in an IP28 IRIX 6.5.22
guest glcheck passes with the reference checksum.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The names the crate uses for itself started at 0x7F000000 and counted
up. Apple's GL hands a program the lowest free name, so that was safe
there; Mesa hands out one above the highest in use, so after a name of
ours it gave the program the next one we were about to take, and the
two were bound to different targets. gltest's SGIS_texture_filter4,
SGIS_sharpen_texture and SGIS_detail_texture checks failed on Linux
with GL_INVALID_OPERATION (glBindTexture target mismatch).

Counting down from u32::MAX leaves Mesa no "one above", and it goes back
to the lowest free name. gltest now passes 167/167 for n32 and o32 on
Linux; nothing changes on the Mac.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
FEATURES.md, iris-gui-README.md, iris-gui's build-info line and TODO.md
said host GL was macOS only. CHANGELOG: the EGL backend, what building and
running it need, and how it was tested. rules/hostgl/linux-egl-and-mesa.md:
the stub-for-every-name problem and avail.rs, Mesa's texture names, c_char,
and what is linked versus opened. The registry README names gl_avail.py.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@techomancer

Copy link
Copy Markdown
Owner

merged in rebased branch

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.

2 participants