Let's add another entry to the list of unusual things running Doom!
Recently, a Nordic employee published his project about Doom running on an nRF5340 MCU. Some videos were published too, and in the longest of them, the gameplay on the first two maps was shown at the default difficulty level (“hurt me plenty”). In such video, the frame rate is generally between 33 and 35 fps, even if some times it falls below the 30 fps threshold on complex scenes.
Such speed is however not surprising: the MCU features a dual core Cortex M33, the main one running at 128 MHz, 512kB RAM and a 96 MHz QSPI interface.
By searching on the net, I also found that this was not the first attempt to port Doom to a Nordic MCU. In fact, a video from 2019 shows a proof of concept of Doom running on the nRF52840. The nRF52840 is much less powerful: it is a single core Cortex M4, running at 64MHz, it has half the RAM of the nRF5340, and the QSPI speed is one third (32 MHz vs 96MHz).
In the 2019 video, Doom runs extremely slow (about 3-5 fps), and many textures are missing, replaced by a placeholder. This is probably because in that video the data is directly fetched from the SD card, which introduces quite a heavy overhead (I can't see an external QSPI).
I wanted to see if I could do better than this. Beside, this was a nice opportunity to increase the list of things that can run Doom.
The Target Device
I did not just want to port Doom to the nRF52840, e.g. using a Nordic development board. Such boards do not have the constraints you will typically find in a finished product, where only few I/O are actually routed out and sconnected to other components and not to "comfortable" pin headers, where you can easily make connections.
What I actually wanted was to:
- find an existing nRF52840-based off-the-shelf device
- modding it by adding all the I/O hardware to being able to run Doom (basically an external QSPI flash and a display, if the device does not come with one)
- port Doom to the modded system.
There are really many devices that are based on the nRF52840, like some gaming mice, however for this project I wanted to find something cheaper. The choice fell on this Bluetooth LE USB dongle.
In my previous projects, I have dealt with systems having limited resources yet giving interesting performance. However miracles can't really be done. Therefore here is a reasonable list of constraints and "permissions":
- A display can be added, if not present. The resolution should be "fairly high" enough. More specifically, I wanted the number of pixels of the 3D viewport to be close the default viewport size on Doom. Noticeably, the default viewport size on Doom is not the full 320x200 resolution (11 screen blocks), and not even the full screen with the status bar (10 screen blocks, i.e. 320 x 168), but it is just it just 9 screen blocks, i.e. 288 x 144 pixels.
- To store the WAD file, an external QSPI memory can be added (no limit in size).
- The WAD can be modified using an external tool (before upload) to rearrange or precalculate data so that it can be easier to handle by the code and it does not need to be (entirely) copied to RAM. However no loss on the details (either in maps, number of "map things" or texture resolution) is allowed. For instance, it is ok to pre-convert the integers to fixed point data, and it is ok to pre-convert composite textures to single patch ones (keeping the final texture detail unchanged). However, reducing the number of enemies in a map, or changing the map by using texture placeholders is not permitted. However, it is ok to change the status bar elements, to adapt to the different resolution.
- The microcontroller cannot be changed. Doom must run on the same...