Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ hashes are given where a change is easiest to understand by reading the commit.
`vpro fbdump`'s files, to diff against a live board's dump, and `vpro
fbdump` adds `objects.bin`.

### VPro on the host GPU (`wgpu` feature, in progress)

- `[graphics] raster = "wgpu"` moves the VPro's raster unit to the host GPU
through wgpu compute (`src/wgpu`, `src/dev/odyssey/wgpu`): filled
triangles, textured or not, clears and the whole back end run as
kernels over board memory in a GPU buffer, word for word with the CPU
pipeline (the golden scenes, the parity tests and the quake1 replay's
checkpoints pass on it); fogged and user-clipped primitives, lines and
points are still shaded on the CPU and stored by the GPU in order. The
front end's own pixel paths work on a mirror the unit keeps current, and
the display composes on the GPU too, read back into the window until the
GUI shares the device. Batches are submitted as the board goes idle, so
the PROM and X painting reach the screen at once, and a swap latches only
after the drawing armed before it is submitted. `vpro gpu` shows the
unit's counters. Window-id writes are kept in step with the front end's
own reads, which fixed GPU-only failures in the visual sweep; the GL
test suites, the visual and line sweeps and the scenes now read back the
same on both raster units.
- `renderer = "wgpu"` (`--renderer wgpu`) draws the window through wgpu;
with the VPro on the GPU unit the frame is presented from the board's
memory without a readback, and Newport's display is composed on the GPU
(the GL compositor's work in WGSL). Branch `wgpu-raster`, plan in
`clanker/plans/wgpu-raster.md`.

### VPro drawing

- Copies from a window into a texture (`glCopyTexSubImage`) read the
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ rex-jit = ["cranelift-codegen", "cranelift-frontend", "cranelift-jit", "cranelif
# engine selected explicitly with REX3_REPLAY_ENGINE=gpu. It never changes the
# live CPU renderer by default.
rex-gpu = ["gputrace", "rex-dirty-upload", "dep:bytemuck", "dep:pollster", "dep:wgpu"]
# Host-GPU rasterisation on wgpu: board memory on the GPU, primitives binned
# into 8x8 tiles and rasterised by compute kernels (src/wgpu, and
# src/dev/<board>/wgpu for each board). See clanker/plans/wgpu-raster.md.
wgpu = ["dep:bytemuck", "dep:pollster", "dep:wgpu"]
# Cranelift-based JIT for IMPACT (GR4) raster shaders: RSS/TE1 pixel
# pipelines monomorphised on a 64-bit key (src/dev/mgras/rss_jit).
gr4-jit = ["cranelift-codegen", "cranelift-frontend", "cranelift-jit", "cranelift-module", "cranelift-native"]
Expand Down
10 changes: 9 additions & 1 deletion FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Select with `cargo build --release --features <comma-separated-list>`; add
| `default` | Default bundle: `tlbvmap` and `rexdiag`. | `tlbvmap`, `rexdiag` |
| `rex-dirty-upload` | Track conservative Newport framebuffer damage and copy/upload only dirty regions. Opt-in; the default retains the original REX3 and presentation paths. | — |
| `rex-jit` | Cranelift compiler for Newport draw shaders; complements precompiled shaders. | `cranelift-codegen`, `cranelift-frontend`, `cranelift-jit`, `cranelift-module`, `cranelift-native` |
| `wgpu` | Host-GPU rasterisation through wgpu compute: the common batch layer in `src/wgpu` and, as they land, the boards' GPU raster units under `src/dev/<board>/wgpu`. Work in progress on branch `wgpu-raster`; see `clanker/plans/wgpu-raster.md`. | `dep:bytemuck`, `dep:pollster`, `dep:wgpu` |
| `rex-gpu` | Experimental, replay-only Newport wgpu/WGSL engine. It does not replace the live CPU renderer and is not part of `gpujit` or release builds. | `gputrace`, `rex-dirty-upload`, `dep:bytemuck`, `dep:pollster`, `dep:wgpu` |
| `gr4-jit` | Cranelift compiler for IMPACT (GR4) raster pipelines (RSS/TE1 shaders). | `cranelift-codegen`, `cranelift-frontend`, `cranelift-jit`, `cranelift-module`, `cranelift-native` |
| `gr2-jit` | Cranelift compiler for GR2 (XZ / Extreme) RE3 span pipelines (SHADED / FLAT shaders). | `cranelift-codegen`, `cranelift-frontend`, `cranelift-jit`, `cranelift-module`, `cranelift-native` |
Expand Down Expand Up @@ -164,6 +165,7 @@ the first `[section]`, because TOML tables continue until the next header.
| `ci_display` | `false` | Keep host graphics window visible in CI mode. |
| `mouse_scroll_pixels_per_line` | `40.0` | Host scrolling distance per guest PS/2 detent. |
| `lock_aspect_ratio` | `true` | Constrain standalone window aspect ratio; false allows letterboxing. |
| `renderer` | `gl` | What draws the window: `gl`, or `wgpu` (needs a build with the `wgpu` feature). With `wgpu` the window is presented through wgpu on the same GPU as a VPro whose raster unit is on the GPU, and its frames are shown without being read back. The iris-gui front end ignores it. |
| `serial_log` | `unset` | Append guest channel-B output (the O2's first serial port); used by CI and GUI serial capture. `-` writes to standard output (for a container's log collector). |
| `serial_log_a`, `serial_log_b` | `unset` | Log what the guest sends on channel A or B when that channel is not the console (A on the Indy and Indigo2, B on the Octane and O2; `serial_log` is the console's). Truncated at start on the Octane and O2, appended to on the Indy and Indigo2, like `serial_log`. |
| `serial_log_timestamps` | `off` | `off` (raw transcript), `wall` (prefix each completed line with a UTC ISO-8601 stamp) or `ecs` (one Elastic Common Schema JSON document per non-empty line, logger `irix.guest`, control characters dropped). A partial line is held until its newline and written on stop. |
Expand All @@ -186,6 +188,8 @@ the first `[section]`, because TOML tables continue until the next header.
| `graphics.resolution` | `guest` | guest, 1024x768, 1280x960, 1280x1024; presets require Newport. |
| `graphics.threads` | `2` | Worker threads the board's fragment pipeline draws on, at least 1. VPro: the screen tiles' owners, besides its front-end and raster threads; more help on a host with cores to spare. |
| `graphics.tile` | `32` | VPro: the fragment pipeline's screen tile size in pixels, a power of two from 8 to 256. Each tile's work runs in order on one worker at a time; with many threads, 64 can be faster. |
| `graphics.raster` | `cpu` | VPro: where the raster unit runs. `cpu` draws on the threads above; `wgpu` draws on the host GPU through wgpu compute (needs a build with the `wgpu` feature; work in progress, see `clanker/plans/wgpu-raster.md`). |
| `graphics.gpu_backend` | `auto` | The wgpu backend for `raster = "wgpu"`: auto, vulkan, metal, dx12 or gl. |

### [scsi.<ID>]

Expand Down Expand Up @@ -433,6 +437,8 @@ iris-gui does not parse this standalone option set; use its named-machine UI.
| `--no-scsi-deferred-int` | Disable deferred SCSI status interrupts (default: enabled for OpenBSD/NetBSD compatibility). |
| `--cpu` | Emulated CPU: `r4400` (default), `r5000`, or `r10000`. Overrides `[machine] cpu`. |
| `--graphics` | Graphics board: `newport` (default), `xz`, `extreme`, `solidimpact`, `highimpact`, `maximpact` (or `impact:solid`, `impact:high`, `impact:max`), `v6`, `v8`, `v10`, `v12` (`vpro:v12` and so on, Octane only). |
| `--raster` | VPro: `cpu` (default) or `wgpu`, the raster unit on the host GPU (`[graphics] raster`; needs a build with the `wgpu` feature). |
| `--renderer` | What draws the window: `gl` (default) or `wgpu` (`renderer`; needs a build with the `wgpu` feature). |
| `--gdb-port` | Enable GDB stub on the given TCP port (e.g. --gdb-port 1234). Connect with: target remote localhost:<port> |
| `--monitor-port` | Monitor console port on 127.0.0.1 (default 8888). Give each iris running at once its own. |
| `--vnc [ADDR]` | Serve the screen, keyboard and mouse over VNC on ADDR (`host:port` or a port; default 127.0.0.1:5900). Sets `vnc.listen`. |
Expand Down Expand Up @@ -566,7 +572,9 @@ and offline-tool controls are included so they are not mistaken for GUI settings
| `MGRAS_REPLAY_TRACE` | IMPACT replay tracing selector (`gputrace` builds). | [src/dev/mgras/record.rs](src/dev/mgras/record.rs) |
| `MGRAS_SIZE_PROBE` | Input for the opt-in IMPACT size/probe test. | [src/dev/mgras/mgras_tests.rs](src/dev/mgras/mgras_tests.rs) |
| `VPRO_GOLDEN` | `tolerance` lets a VPro golden frame whose hash changed pass inside its stored limits. | [src/dev/odyssey/golden/mod.rs](src/dev/odyssey/golden/mod.rs) |
| `VPRO_REPLAY` | Replay input path for one VPro recording; `VPRO_REPLAY_DIR` picks the directory that is replayed otherwise, `VPRO_REPLAY_THREADS=1` replays with the raster and back-end threads running, `VPRO_REPLAY_LIMIT=<n>` stops after n records (with `VPRO_REPLAY_DUMP` to see the board there). | [src/dev/odyssey/golden/replay.rs](src/dev/odyssey/golden/replay.rs) |
| `IRIS_WGPU_DUMP` | With the `wgpu` feature: a directory the composed WGSL of every GPU kernel is written to as it is compiled, for `naga` and `spirv-val`. | [src/wgpu/shader.rs](src/wgpu/shader.rs) |
| `VPRO_GPU_TRACE_WAITS` | With the `wgpu` feature: the VPro GPU unit counts its readbacks by the board function that caused them; the replay prints the histogram. | [src/dev/odyssey/wgpu/mod.rs](src/dev/odyssey/wgpu/mod.rs) |
| `VPRO_REPLAY` | Replay input path for one VPro recording; `VPRO_REPLAY_DIR` picks the directory that is replayed otherwise, `VPRO_REPLAY_THREADS=1` replays with the raster and back-end threads running, `VPRO_REPLAY_GPU=1` replays on the GPU raster unit (`wgpu` feature; with `VPRO_REPLAY_LAZY=1` the unit skips the readbacks between checkpoints, to time the GPU path alone), `VPRO_REPLAY_LIMIT=<n>` stops after n records and checks only the checkpoints reached (with `VPRO_REPLAY_DUMP` to see the board there), `VPRO_REPLAY_TICK=<n>` ends a board slice every n stores as a running machine does (on the GPU unit each slice end can submit a batch), `VPRO_REPLAY_WRITES=<file>` logs every write the board makes to guest memory (pixel reads, fences, query results); the report line gives their hash and the final memory hash, to compare two replays. `VPRO_GPU=1` puts every VPro test board (the golden scenes among them) on the GPU raster unit. | [src/dev/odyssey/golden/replay.rs](src/dev/odyssey/golden/replay.rs) |

`RUST_LOG` configures the host Rust logger; `IRIS_DEBUG_LOG` configures device
logging. Standard host variables (`HOME`, `XDG_CACHE_HOME`, `LOCALAPPDATA`,
Expand Down
73 changes: 73 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ How each board uses it:
| Newport (`src/dev/ng1/`) | REX3 register writes, GO commands, HOSTRW data | `REX3-Processor` executes REX3 drawing; `REX3-Refresh` composites VC2/XMAP/CMAP scanout and owns the GL context |
| GR2 (`src/dev/gr2/`) | HQ2 command FIFO (`hq_fifo`) | `GR2-HQ2` interprets HQ2/GE commands at the GL level and feeds `re3_fifo`; `GR2-RE3` rasterises; `GR2-Display` scans out |
| IMPACT (`src/dev/mgras/`) | HQ3 command FIFO and direct raster writes (`hq_fifo`) | `MGRAS-HQ3` runs the command and geometry front end and feeds `rss_fifo`; one RSS thread per raster engine draws (Maximum IMPACT: two, each owning alternate rows, fed by the broadcast FIFO); a refresh thread scans out |
| VPro (`src/dev/odyssey/`) | the command FIFO (`fifo.rs`) | `odyssey-work` decodes, transforms and sets primitives up and hands them to the raster queue; `odyssey-raster` and its `odyssey-pool` tile workers rasterise, texture and store; the display thread scans out. With `[graphics] raster = "wgpu"` the raster and pool threads do not start: the front end records into batches the host GPU draws (below) |

GR2 and IMPACT are emulated at a high level: their microcode is uploaded but
not executed; the command and GL token stream is interpreted directly. All
Expand All @@ -496,6 +497,78 @@ without locks and composes it through the board's colour maps and DAC.
Everything that touches the host GL context (including teardown) runs on the
thread that owns it.

### The raster unit on the host GPU (`wgpu` feature)

With `[graphics] raster = "wgpu"` the VPro's raster unit runs as compute
kernels on the host GPU (`src/wgpu` is the common layer, `src/dev/odyssey/
wgpu` the board's kernels; the design is `clanker/plans/wgpu-raster.md`).
The front end runs every draw inside its submit and appends one *record*
per primitive (a triangle's setup, a quad it shaded itself, a clear's
rectangle, a row of pixels of an image put) to a *batch*, with the state
each was issued under in a *slot*; a batch is two dispatches, a bin pass
that gives each 8x8 tile a bitmask over the batch's primitives and a raster
pass with one workgroup per busy tile that walks its mask in order and
stores pixels itself. Order per pixel is the record order, so the kernels
need no atomics on pixels and no synchronisation between workgroups.

Board memory lives in a GPU buffer; nothing on the CPU maps it. The front
end's own pixel paths (image puts, the integer primitives and their logical
operations, the X server's window-id writes, plane clears, DMA puts, depth
and stencil image writes) store through the unit as *blit* records, so they
keep their place in the order and nothing waits. The board's `Vram` is a
*mirror* for what still runs on the CPU (copies, DMA gets, the hashes and
the monitor), kept in step by two dirty maps: CPU stores mark 16 KB blocks
that go up before the next batch and at the end of each run of stores, and
records mark the blocks their boxes touch; a reader brings back only the
marked blocks under the box it reads (`Board::planes_in`), `dest_mem` and
`gpu_sync` bring everything back, and `Board::raster_wait` on this unit is a
flush and a GPU wait with no readback. A batch is submitted when it fills,
when something waits, and at the end of a run of stores once the board is
going idle or the batch has been open a few milliseconds
(`GpuRaster::tick`), so the screen follows the guest as it would on the
board without a submission per primitive. Swaps follow the same order
without a wait: a swap arm notes its position in the command stream
(`CommandFifo::pushed`), the unit publishes the position its submissions
cover (`DisplayShared::covered`), and the retrace latches a flip only once
the drawing before its arm is submitted; the scanout's own submission
queues behind that drawing on the same GPU queue. The invariant for the mirror is the
one the raster thread already imposed: the front end waits before it touches
memory itself; paths that must not wait (texture and scratch uploads) store
through an untracked pointer and upload exactly the tiles they wrote. The
display thread composes on the GPU as well (`src/wgpu/scanout.rs`), from
the same buffer, and reads the frame back into `Rex3Screen`; `Frame::compose`
on the CPU remains for screenshots and the replay harness.

The front end reads the window-id buffer's bytes itself, without waiting
for the raster unit (the drawable format for a read, the ownership test).
So when it sends a store into those byte fields to the GPU it makes the
same store in the mirror, untracked: the mirror's id bytes stay current
with nothing uploaded twice. A new path that writes them on the GPU must do
the same.

With `renderer = "wgpu"` the window is drawn by `src/wgpu/present.rs`
(`Presenter`: the surface, a pipeline of textured quads for the frame, the
debug overlay and the status bar, and a small kernel that unpacks a frame
already on the device). The surface comes from the instance the board's
`Gpu` was opened on (`GfxDisplay::gpu`). The VPro's display thread then
composes on the GPU and hands the scan-out buffer to the renderer
(`Renderer::present_gpu_frame`) unless something wants `screen.rgba` filled
(a screenshot, a frame request, a capturing VNC client), in which case it
reads the frame back as before. Newport's planes are composed on the
GPU too, by `src/dev/ng1/wgpu` (the GL and software compositors' rules as a
kernel on the common scan-out driver, checked pixel for pixel against
`SwCompositor`), and presented from there. The other boards' frames are
uploaded from the CPU.

The kernels are held to the CPU model word for word by parity tests
(`cargo test --features wgpu --lib odyssey::wgpu`), the golden scenes with
`VPRO_GPU=1` and the replay checkpoints with `VPRO_REPLAY_GPU=1`. Two rules
came out of that and live in `clanker/wgpu/`: a GPU's division is not
correctly rounded and its compiler fuses multiply-adds, so the kernels
divide through `div_exact` and keep products apart from sums; and a large
struct copied by value into a lane's private memory gave silently wrong
results, so records, slots and tables are read from their buffers in place.

The status bar under the frame is fed by `StatusSource` (`src/disp.rs`),
which the machine builds once and hands to every graphics board: the
heartbeat word (front-panel LEDs and SCSI/network activity bits), the CPU
Expand Down
5 changes: 5 additions & 0 deletions HELP.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ standalone `iris` window if you need them.

The window is freely resizable. At 1× and 2× scale the display is rendered with nearest-neighbour pixel-perfect sampling; at all other sizes trilinear filtering with mipmaps is used. `lock_aspect_ratio = false` in `iris.toml` allows free resizing with letterboxing. When the guest changes video mode (PROM, then IRIX during boot) the window snaps to the new resolution times the configured `scale` / `--2x`.

`renderer = "wgpu"` (or `--renderer wgpu`, in a build with the `wgpu` feature) draws the window through wgpu instead of OpenGL, with the same scaling rules. With a VPro whose raster unit is on the GPU (`raster = "wgpu"`) the window shares that GPU, and each frame goes from the board's memory to the screen without passing through main memory; screenshots and VNC still read frames back when they ask for them. Newport's display (Indy, Indigo2 XL) is composed on the GPU as well. If wgpu cannot open the window, iris says so and draws with OpenGL.

## VNC

iris has a built-in VNC server. Any VNC viewer (TigerVNC, RealVNC, Remmina,
Expand Down Expand Up @@ -602,6 +604,7 @@ scale = 1 # window scale, kept across guest mode changes; --2x
headless = false # no window, no graphics; the O2 boots on its serial console (audio unaffected)
no_audio = false # no HAL2 / cpal (graphics unaffected)
lock_aspect_ratio = true # false = free resize with letterboxing
# renderer = "gl" # "wgpu": draw the window through wgpu (builds with the wgpu feature)
mouse_scroll_pixels_per_line = 40

nat_subnet = "192.168.0.0/24" # gateway .1, guest .2
Expand Down Expand Up @@ -636,6 +639,8 @@ heads = 1 # 2 = dual-head Newport (second REX3 in GIO slot 1)
resolution = "guest" # or "1024x768", "1280x960", "1280x1024"
# threads = 2 # VPro: fragment pipeline workers (more on a host with cores to spare)
# tile = 32 # VPro: screen tile size, a power of two from 8 to 256
# raster = "cpu" # VPro: "wgpu" draws on the host GPU (builds with the wgpu feature; work in progress)
# gpu_backend = "auto" # with raster = "wgpu": "vulkan", "metal", "dx12" or "gl"

[clock]
# fixed_mhz = 33 # Count MHz: default 33 on IP22/IP24, 97.5 on IP28
Expand Down
Loading
Loading