File size: 3,469 Bytes
7c15b94
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6eccfab
7c15b94
6eccfab
 
7c15b94
6eccfab
 
6bff818
 
 
6eccfab
6bff818
6eccfab
2c00621
 
 
6eccfab
 
7c15b94
6eccfab
 
 
 
7c15b94
6eccfab
7c15b94
6eccfab
 
 
 
 
 
7c15b94
 
 
 
 
 
 
 
 
 
6eccfab
7c15b94
 
 
 
 
 
 
 
 
 
6eccfab
7c15b94
 
6eccfab
7c15b94
 
6eccfab
 
7c15b94
6eccfab
7c15b94
6eccfab
 
 
 
 
 
 
7c15b94
6eccfab
7c15b94
 
 
6eccfab
 
 
7c15b94
 
 
6eccfab
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
---

license: gpl-2.0
library_name: onnxruntime
tags:
  - onnx
  - emulation
  - cpu
  - riscv
  - doom
  - retro
  - art
  - cursed
language:
  - en
pipeline_tag: other
inference: false
---


# Doom on ONNX

![Doom DEMO1 rendered by the ONNX model](doom.gif)

A single self-contained ONNX model (`doom.onnx`, ~8.4 MB) that, when run on
**any** ONNX Runtime CPU EP, boots and renders the original 1993 *Doom*.
No custom operators, no execution-provider plugins, no Python in the loop —
just standard ONNX ops (Add, BitwiseAnd, Where, Gather, ScatterElements,
Loop, If, …) executing inside a single `InferenceSession.run` call.

The model contains:
- an **RV32IM CPU** built entirely out of ONNX operators,
- the **doom1.wad** shareware game data as a read-only initializer,
- the **doomgeneric** Doom source cross-compiled to bare-metal RV32IM and
  baked into RAM as another initializer.

## Reference render

The `doom.gif` in this repo was assembled from 74 PNG frames captured during
a single `InferenceSession.run` invocation:

- **Total**: 80,000,000 RV32IM instructions, **10.8 hours wall time**
- **Rate**: 1,562 IPS (init code) → 2,053 IPS (in-game rendering)
- **Reached**: title wipe → menu → DEMO1 load → game logic → **3D BSP

  rendering of actual gameplay** (frames 54–75)

## Performance

~2,000 simulated RV32IM instructions per second on a modern laptop CPU.
This is **not** a real-time emulator. One frame every ~9 minutes is the
reality on a single CPU thread. See `PERF_INVESTIGATION.md` in the source
repo for the full investigation (TL;DR: ORT's `MayInplace` alias doesn't
apply to Loop-carried state, so the 8 MiB RAM gets fully copied per
`ScatterElements`).

## Running

```python

import numpy as np

import onnxruntime as rt



sess = rt.InferenceSession("doom.onnx", providers=["CPUExecutionProvider"])



RAM_SIZE = 8 * 1024 * 1024

ram = np.load("initial_ram.npy")  # Doom ELF baked into RAM

pc  = np.array(0x1000, dtype=np.int32)

regs = np.zeros(32, dtype=np.int32)



MMIO_TICK = RAM_SIZE - 16

sim_ms = 0

for chunk in range(250):

    ram[MMIO_TICK:MMIO_TICK + 4] = np.frombuffer(

        np.uint32(sim_ms).tobytes(), dtype=np.uint8)

    sim_ms += 100

    pc, regs, ram = sess.run(None, {

        "pc_in": pc, "regs_in": regs, "ram_in": ram,

        "trip_count": np.array(100_000, dtype=np.int64),

    })

    # framebuffer at RAM_SIZE - 32 - 64000, 320×200 palette indices

```

The host only writes a millisecond counter into the MMIO tick register
between chunks and reads the framebuffer out of the returned `ram_out`.

## Inputs / outputs

| Name | Type | Shape | Role |
|---|---|---|---|
| `pc_in` | int32 | scalar | program counter |
| `regs_in` | int32 | [32] | x0..x31 (x0 forced to 0) |
| `ram_in` | uint8 | [8 MiB] | full writable memory |
| `trip_count` | int64 | scalar | how many insts to execute |
| `pc_out` / `regs_out` / `ram_out` | (same types) | | post-state |

`rom` (the WAD) is a read-only initializer baked into the model.

## License

The CPU + DMA glue is MIT. doomgeneric and the original Doom source are
GPL-2.0; the shareware doom1.wad ships under id Software's shareware
terms. This model card and model file inherit GPL-2.0.

## Acknowledgements

id Software for releasing Doom's source. The doomgeneric project for the
platform-agnostic port. The ONNX team for an op set this absurdly expressive.