Skip to content

octane: the VPro (Odyssey) graphics boards - #362

Merged
techomancer merged 10 commits into
techomancer:mainfrom
jamesbraid:octane-vpro
Nov 2, 2026
Merged

techomancer merged 10 commits into
techomancer:mainfrom
jamesbraid:octane-vpro

Conversation

@jamesbraid

@jamesbraid jamesbraid commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Adds the Octane's VPro (Odyssey) graphics boards, ported from my sgi-origami QEMU branch. An Octane2 with a V12 boots IRIX 6.5.30 to the 4Dwm desktop on one or two CPUs, and OpenGL works: Smoke & Mirrors, OpenGL Performer's truck demo, Inventor's ivview, SGI Quake and the gltest suites all draw.

To use one, pick the board in the machine's config:

[graphics]
board = "v12"   # or "v6", "v8", "v10"

IMPACT stays the default, and other machines refuse these values. IRIX needs an install with the VPro kernel driver and X module; an IMPACT install finds no screen.

pr362-final-cpu2-nologin pr362-final-cpu2-gltest pr362-final-cpu2-mirrors pr362-final-hinv-quake

Speed

The first port drew under a board lock on the CPU thread. Most of the work since has been making it fast without that lock. Frames per second on an Octane2 V12 with IRIX 6.5.30:

first port this PR
Smoke & Mirrors 0.4 5.9
Performer truck 13.5 36.7
SGI Quake timedemo (640x480 window) didn't run 3.8
SGI Doom timedemo didn't run 71.8

Design

  • No lock around the board. Four threads touch it: the processors, the front end (decodes the command FIFO and owns the board's state), the raster unit (owns the planes) and the display (reads the frame through its own reader). Command stores queue in the shared GFifo at the board's real depth. Waiting reads and swaps keep the order the driver relies on, and two processors may store to the FIFO. gfifo gains peek_nth_at, peek_run_at and consume_n_at so the front end can take a run of stores with one head publish. clanker/odyssey has the thread and ordering notes, and HACKING.md lists the new threads.
  • Shared GL core. Transform and lighting are the existing gl code, now generic over a compile-time Geometry. Frustum is today's pipeline for GR2 and IMPACT. NearW is the VPro's: a nearest-rounded reciprocal and a multiply for the divide, truncation onto the 1/256 subpixel grid, and clipping on x, y and a near-w plane only, with near and far left to a per-fragment depth kill. The existing methods are the Frustum instantiation and monomorphise to the same arithmetic. A randomised differential of 200,000 polygons and lines hashed identically before and after, so IMPACT output is unchanged. Lighting also gets a Default.
  • Typed texture sampler in the board's texturing path, rather than raw word access.
  • The rest of the board is arranged by function: widget registers and identity, memory and DMA, the FIFO interpreter, integer drawing, off-screen objects, pixel DMA and scanout.

The Octane2 parts

The board sits on widget 11, where IMPACT otherwise goes. The V10 and V12 only shipped in the Octane2, so choosing one makes the machine an Octane2: Crossbow revision code 5 and a revision 4 Bridge (the Octane's are code 4 and revision 3), which the PROM's board detection reads. There is no separate machine option. The Bridge revision is now settable and survives a reset. iris warns at start-up if the PROM image is older than the board needs (4.5 for V10/V12, 4.0 for V6/V8, from SGI's upgrade-kit notes).

These registers and the board's bus-visible behaviour follow what the PROM and driver read and write. Where the hardware is not known, the board makes a stated model choice. The Crossbow reset fences, for instance, hold their values but gate nothing, because no warm reset travels through the modelled switch.

Testing

  • Octane2 V12, IRIX 6.5.30: boots to 4Dwm on one and two CPUs; mirrors, perfly, ivview and gltest draw.
  • pixtest passes 323 of 380 cases and glstate 410 of 415. The failures are colour-index mode, pixel scale-bias and maps, and RGBA8 textures from byte, short and int data; they're the same before and after this rework. The VPro expectations follow established library behaviour (raster distance, the depth range's 2S factor, the RGB short-read row stride), with no expected-failure entries.
  • The points, lines, triangle and alpha frames of the board diagnostic's expected output match exactly at 1280x1024.
  • cargo test --release --lib --features jitv2: 2795 passed.
  • Draw-level golden frames (91, bit-exact by default) drive Board::gl_draw the way the GL context does and hash colour, depth and stencil, with a vertex golden for eye, clip and window positions and lit colours. scripts/vpro-goldens.sh has the commands, and testdata/README.md says when re-taking is right.
  • Headless replay feeds an IRIS_ODSY_REC recording through a fresh board and hashes the planes and object memory. Recordings stay out of git, so without one the test prints SKIPPED.
  • The vpro monitor command counts bus refusals and display work.

Not in this PR

The Octane's VPro boards (V6, V8, V10, V12), ported from
hw/misc/sgi-odyssey.c in jamesbraid/qemu-sgi-origami and arranged by
what each part does: widget registers and identity, memory and DMA, the
command FIFO interpreter, integer drawing, the GL path into a scalar
rasteriser, off-screen objects, texturing, pixel DMA and the scanout.

Four threads reach the board with no lock around it: the processors,
the front end, the raster unit and the display. Command stores
queue in the shared GFifo at the board's real depth; the front end (BUZZ)
decodes them and owns the board's state; the raster unit owns the planes;
and the display reads the frame through its own reader, as the display
and the rendering engine reach memory together on the board. Waiting
reads and swaps keep the order the driver relies on, and two processors
may store to the FIFO; a bus access the board refuses ends the front
end's short pause before a run. The `vpro` monitor command counts bus refusals and
display work, and IRIS_ODSY_REC records a session's bus accesses.

The display reports its hardware cursor's position and opaque pixels to the
host's pointer steering, as the other graphics boards do.

Tests cover identity through both widget windows, each product's
revision and memory, the in-order queue, interrupts,
swaps, DMA, the object store, pixel paths and the rasteriser. The agent
notes under clanker/odyssey say how the threads and their ordering rules
fit together.
`[graphics] board` takes v6, v8, v10 and v12 (and vpro: aliases) and puts
the board on widget 11, where the IMPACT board otherwise sits, with both
widget windows mapped and the Crossbow link up. IMPACT stays the Octane's
default and the other machines refuse the values. IRIX needs an install
with the VPro kernel driver and X server module; one installed with an
IMPACT board finds no screen.

SGI's upgrade kit (007-4210-003) needs Crossbow 1.4, IRIX 6.5.10 and
PROM 4.5 for the V10 and V12, and PROM 4.0 for the V6 and V8. The V10
and V12 shipped only in the Octane2, so choosing one makes the machine
an Octane2: it presents the Octane2's Crossbow (revision code 5) and a
revision 4 Bridge; the Octane's are code 4 and revision 3. There is no
separate machine option.
iris warns at start-up when the PROM image is older than the board needs.
HACKING.md lists every thread a device runs; it gains the VPro's.
A board that builds a Lighting each vertex had only mem::zeroed() to
start from, which needs unsafe at every call site. Material's specular
table is longer than the arrays Default is derived for, so it gets a
written-out all-zero Default; Light and Lighting derive theirs. The
values are the zeroed ones, so init() still sets OpenGL's initial state
on top.
peek_nth_at reads the entry n places behind a head without consuming
it, peek_run_at copies the waiting entries after one look at the tail,
and consume_n_at retires a run with at most one publication of the head.
A consumer that handles queued stores in runs would otherwise pay an
acquire load and a head publish per entry. Tests cover a look and a run
across the wrap.
@jamesbraid
jamesbraid marked this pull request as draft October 30, 2026 10:40
@jamesbraid jamesbraid changed the title octane: the VPro V12 graphics board WIP: octane: the VPro V12 graphics board Oct 30, 2026
@jamesbraid

Copy link
Copy Markdown
Collaborator Author

don't merge; still working on refactoring :)

The VPro expectations in pixtest and glstate follow what the library is
established to do: the raster distance, the depth range's 2S factor in
the depth expectation, and the library's row stride for RGB short reads.
@techomancer

Copy link
Copy Markdown
Owner

in actual hardware the display and rendering engine talk to memory at the same time and there is are not mut borrows or locks.
the safety and synchronization are guaranteed by software programming the hardware to do buffer swapping or syncing using fences, synchronization bits and such. trying to be more safe than that doesn't buy us anything besides slowing the implementation down.
if we see synchronization problems it means we didnt implement the behavior correctly.
as a corollary we often see flashes of wrong planes and colors on other boards. funnily enough they do happen on real hardware too.

@jamesbraid
jamesbraid force-pushed the octane-vpro branch 2 times, most recently from 58f54b3 to ccb6378 Compare October 31, 2026 17:06
@jamesbraid jamesbraid changed the title WIP: octane: the VPro V12 graphics board octane: the VPro (Odyssey) graphics boards Oct 31, 2026
@jamesbraid
jamesbraid marked this pull request as ready for review October 31, 2026 17:56
@jamesbraid
jamesbraid marked this pull request as draft October 31, 2026 17:59
Nothing pinned what the VPro vertex side and rasteriser put in the
planes, so a change to the draw path or the transform could move frames
unnoticed. Scenes drive Board::gl_draw the way the GL context does and
hash their colour, depth and stencil planes into scenes.tsv, with the
planes kept as PNGs; a vertex golden pins eye, clip and window positions
and lit colours. The default check is bit-exact; VPRO_GOLDEN=tolerance
passes a frame that moved inside per-scene limits. Tests check that the
scenes cover what they claim, so a golden cannot pin a blank frame.
The goldens cover the rasteriser on drawn scenes, not what the IRIX
stack sends: command streams, DMA uploads, windows and texture state.
The replay feeds an IRIS_ODSY_REC recording's stores through a fresh
board and answers its DMA reads from the recording, hashing both colour
buffers, depth, stencil and object memory every 500,000 records against
testdata/replay. The recordings are tens of MB and stay out of git; with
none present the test prints SKIPPED and passes.
The VPro's regression nets are plain cargo tests with environment
variables to pick the mode. scripts/vpro-goldens.sh names the commands:
the frames and vertices (bit-exact check, a tolerance mode that passes a
frame that moved inside its stored limits, and the re-take), and the
replays with their re-take. testdata/README.md says what each net pins
and when re-taking one is right and when it would hide a bug.
@jamesbraid
jamesbraid force-pushed the octane-vpro branch 4 times, most recently from 7923f06 to ef09d41 Compare November 1, 2026 04:57
Boards differ in how their geometry engine divides by w, puts window
positions on the subpixel grid and which view-volume planes clip. Add a
Geometry trait with two instantiations: Frustum, today's OpenGL pipeline
for GR2 and IMPACT, and NearW for VPro, which divides by a nearest-rounded
reciprocal and a multiply, truncates onto the grid, and clips only x, y and
a near-w plane, leaving near and far to a per-fragment depth kill.

The clipper, outcode and viewport mapping take the geometry and the vertex
type as type parameters (ClipVertex), so a board with its own vertex type
can share them. A vertex type may carry polygon-mode edge flags, which the
clipper then keeps, and the clipper reports when a polygon outgrows its
workspace. The existing methods are the Frustum instantiation over Wv and
monomorphise to the same arithmetic: a randomised differential of 200,000
polygons and lines through outcode, dist, clip_polygon, clip_line and
project hashes identically before and after.
An Octane2's PROM programs registers the earlier Crossbow lacks: a reset
fence for the core and one per link at 0x78 to 0xb8, and a resource lock
at 0xc0 with its clear at 0xc8. Answering them with an access error
leaves the PROM off its path. The fences reset to 0xff and keep bits 7:0,
the lock takes a value only while it is free, and a write to its clear
frees it. Code 4
and earlier keep the range reserved. The fences gate nothing, because no
warm reset travels through the modelled switch.

The Bridge reported revision 1 always, but the chip is revision 3 on
the 175 MHz Octane (an OpenBSD boot prints it) and 4 on the reference
Octane2 (its PROM monitor read 0x4c00206d). The machine can now set the
revision, which survives a reset as the chip's mask does.
@jamesbraid
jamesbraid force-pushed the octane-vpro branch 4 times, most recently from 79fb4aa to e38fb34 Compare November 1, 2026 21:49
@jamesbraid
jamesbraid marked this pull request as ready for review November 1, 2026 22:16
@Samueltgg12

Copy link
Copy Markdown
Contributor

@techomancer review time. it's gonna be big.

@techomancer
techomancer merged commit c6dea8c into techomancer:main Nov 2, 2026
7 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.

3 participants