Skip to content

Commit 91ecd7f

Browse files
committed
q5k: makefiles, including qrepack, writeup
1 parent b23aa87 commit 91ecd7f

21 files changed

+1470
-154
lines changed

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "demos/q5k/qrepack/LibSL-small"]
2+
path = demos/q5k/qrepack/LibSL-small
3+
url = https://github.com/sylefeb/LibSL-small.git

README.md

+52-13
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
1-
# TinyGPUs
2-
*Making graphics hardware like its 1990, explained*
1+
# TinyGPUs: the DMC-1
2+
*Making graphics hardware like its 1990, explained. Renders Doom, Comanche and Quake levels!*
33

4-
> **This is work in progress**. You're coming too soon, but feel free to peek around!
4+
> **This is work in progress**. Please stay tuned if you'd like to known when additional explanations come in. Comments welcome!
55
66
Quick links:
7-
- [TinyGPUs](#tinygpus)
7+
- [TinyGPUs: the DMC-1](#tinygpus-the-dmc-1)
88
- [Running the demos](#running-the-demos)
99
- [In simulation](#in-simulation)
1010
- [On the MCH2022 badge](#on-the-mch2022-badge)
1111
- [On the icebreaker](#on-the-icebreaker)
1212
- [The DMC-1 design](#the-dmc-1-design)
1313
- [Context](#context)
1414
- [On perspective correct texturing](#on-perspective-correct-texturing)
15-
- [A key reference](#a-key-reference)
15+
- [Z-constant rasterization](#z-constant-rasterization)
16+
- [Precomputing the per-pixel division](#precomputing-the-per-pixel-division)
17+
- [A key reference](#a-key-reference)
18+
- [On lightmaps](#on-lightmaps)
1619
- [Design walkthrough](#design-walkthrough)
1720
- [Discussion](#discussion)
1821
- [Credits](#credits)
1922

20-
The tinyGPUs project started with the following question: *"What would have resembled graphics hardware dedicated to our beloved retro-games from the early 90's, such as Doom 1993 and Comanche 1992?"*. This led me to creating the `DMC-1` GPU, the first (and currently only!) tinyGPU in this repository.
23+
The tinyGPUs project started with the following question: *"What would have resembled graphics hardware dedicated to our beloved retro-games from the early 90's, such as Doom 1993 and Comanche 1992?"*. This led me to creating the `DMC-1` GPU, the first (and currently only!) tiny GPU in this repository.
2124

2225
> `DMC` stands for *Doom Meets Comanche*... also, it sounds cool (any resemblance to a car name is of course pure coincidence).
2326
@@ -36,13 +39,15 @@ For building the DMC-1 demos Silice has to be installed and in the path, please
3639

3740
> **Note:** The build process automatically downloads files, including data files from external sources. See the download scripts [here](hardware/common/download_all.sh) and [here](demos/data/get_data.sh).
3841
39-
There are three main demos: `terrain`, `tetrahedron` and `doomchip-onice`.
42+
There are several demos: `terrain`, `tetrahedron`, `doomchip-onice`, `interleaved`, `triangles` and `q5k` (quake viewer!).
4043
All can be simulated, and currently run on the [mch2022 badge](https://www.bodge.team/docs/badges/mch2022/) and the [icebreaker](https://1bitsquared.com/products/icebreaker) with a SPI screen plugged in the PMOD 1A connector (details below).
4144

4245
<table align="center"><tr>
4346
<td><img src="docs/terrain.png" width="160px"></td>
44-
<td><img src="docs/tetrahedron.png" width="160px"></td>
4547
<td><img src="docs/doomchip-onice.png" width="160px"></td>
48+
<td><img src="docs/quake.jpg" width="160px"></td>
49+
</tr><tr>
50+
<td><img src="docs/tetrahedron.png" width="160px"></td>
4651
<td><img src="docs/interleaved.png" width="160px"></td>
4752
<td><img src="docs/triangles.png" width="160px"></td>
4853
</tr></table>
@@ -72,6 +77,12 @@ cd demos
7277
make simulation DEMO=doomchip-onice
7378
```
7479

80+
For the quake-up5k (q5k) demo:
81+
```
82+
cd demos
83+
make simulation DEMO=q5k
84+
```
85+
7586
___
7687
### On the MCH2022 badge
7788

@@ -86,6 +97,9 @@ The `program_all` target takes time as it uploads the texture pack. Once done,
8697
use `program_code` to only upload the compiled code and `program_design` for the
8798
design only (as long as there is power to the badge).
8899

100+
> When switching between the q5k (Quake) and other demos, use `make clean` to
101+
> ensure the correct palette is used next.
102+
89103
___
90104
### On the icebreaker
91105

@@ -140,6 +154,8 @@ The `DMC-1` is my take on this. Thinking beyond Doom, I thought I should also su
140154
- Adding a terrain to Doom sounds like a huge thing.
141155
- If I was to create a GPU for Doom, it better should come with a killer feature.
142156

157+
> And while I was at it, I later added support for Quake level rendering!
158+
143159
In terms of resources, I decided to primarily target the Lattice ice40 UP5K. First, this is the platform used by [the incredible source port on custom SOC by Sylvain Munaut](https://www.youtube.com/watch?v=3ZBAZ5QoCAk). Targeting anything bigger would have seemed too easy. Second, the UP5K is fairly slow (validating timing at 25 MHz is good, anything above is *very* good), and has 'only' 5K LUTs (that's not so small though, [1K LUTs can run a full 32 bits RISCV dual-core processor!](https://github.com/sylefeb/Silice/blob/master/projects/ice-v/IceVDual.md)). So this makes for a good challenge. On the upside, the UP5K has 8 DSPs (great for fast multipliers!) and 128KB of SPRAM, a fast one-cycle read/write memory. So this gives hope something can actually be achieved. Plus, a SPIflash memory is typically hooked alongside FPGAs for its configuration. A SPIflash is to be considered read only for our purpose (because writing is very, very slow), but even though reading takes multiple cycles to initialize a random access, performance is far from terrible. And that's great, because SPIflash memories are typically a few MB and we need to put our large textures somewhere!
144160

145161
At this point, you might want to watch [my video on the Doomchip on-ice](https://youtu.be/2ZAIIDXoBis), [browse the slides here](https://www.antexel.com/doomchip_onice_rc3/) or read the [detailed design walkthrough](docs/DMC-1-walkthrough.md). This explains how the initial design of the `DMC-1` was achieved, including perspective correct texturing for walls and flats (floors and ceilings) as well as the terrain rendering.
@@ -164,11 +180,15 @@ This is more general because clearly the perspective is not 'axis aligned' with
164180

165181
> I call *free texture mapping* the general case where u,v coordinates would be assigned to the triangle vertices and used to interpolate texture coordinates across the triangle surface. This interpolation also requires the per-pixel division, but I don't have a clever trick to escape this one!!
166182
183+
#### Z-constant rasterization
184+
167185
One possible approach to deal with this case is the so called *z-constant raterization*. This is not a very common technique and, afaik, there are not many descriptions or implementations of it. I found a description in this article: [Free Direction Texture Mapping](http://qzx.com/pc-gpe/fdtm.txt) by Hannu Helminen. It is part of a highly enjoyable treasure trove, an archive of the *PC Game Programmer's Encyclopedia*.
168186

169-
> As I browsed the PCGPE archive (highly recommended) I found [a great article on texturing](http://qzx.com/pc-gpe/texture.txt) by Sean Barrett. This article from 1994, which covers many aspects of texturing, introduces an approach for planar texturing mapping. What I describe below is equivalent, and it turns out his approach has been used in many games around the Quake era! [I come back to it later](#a-key-reference).
187+
> As I browsed the PCGPE archive (highly recommended) I found [a great article on texturing](http://qzx.com/pc-gpe/texture.txt) by Sean Barrett. This article from 1994, which covers many aspects of texturing, introduces an approach for planar texturing mapping. What I describe next is equivalent, and it turns out his approach has been used in many games around the Quake era! [I come back to it later](#a-key-reference).
188+
189+
#### Precomputing the per-pixel division
170190

171-
Z-constant raterization sounds good but I was not too keen on implementing it. Then, I looked back at how I dealt with flats in the *Doomchip onice*. You see, because I am drawing *only* vertical span, producing the screen column after column, I could not do horizontal spans like the original engine. Thus I had to deal with the perspective effect during texturing, and avoid the per-pixel division. Well, I did not *avoid it*, because it is required, but instead I *precomputed* it. Here is a figure detailing the idea (my talk features an [animated version](https://youtu.be/2ZAIIDXoBis?t=1313)):
191+
Z-constant raterization sounds good but I was not too keen on implementing it. Then, I looked back at how I dealt with flats in the *Doomchip onice*. You see, because I am drawing *only* vertical span, producing the screen column after column without a framebuffer, I simply cannot draw horizontal spans like the original engine. Thus I had to deal with the perspective effect during texturing, and avoid the per-pixel division. Well, I did not *avoid it*, because it is required, but instead I *precomputed* it. Here is a figure detailing the idea (my talk features an [animated version](https://youtu.be/2ZAIIDXoBis?t=1313)):
172192

173193
<center><img src="docs/fig_inv_y.png" width="600px"></center>
174194

@@ -210,19 +230,36 @@ And that's about it for the main trick enabling perspective correct *planar* tex
210230
```
211231
Planar spans are only used for horizontal floors/ceilings in this demo, since walls use simpler vertical spans. Because the surfaces are horizontal, the parameters to `PARAMETER_PLANE_A` are `256,0,0`: only the normal varies with the $y$ axis.
212232
213-
A more general use can be seen in the [tetrahedron demo](demos/tetrahedron/tetrahedron.c). In this demo the triangles are rasterized into spans on the CPU ; this can be seen in the file [raster.c](software/api/raster.c) that contains detailed comments on that process too.
233+
A more general use can be seen in the [tetrahedron demo](demos/tetrahedron/tetrahedron.c). In this demo the triangles are rasterized into spans on the CPU ; this can be seen in the file [raster.c](software/api/raster.c) that contains detailed comments on that process too. And of course, that is also the case in the [Quake viewer demo](demos/q5k/q5k.c).
214234
215-
### A key reference
235+
#### A key reference
216236
217237
In [this 1994 article](http://qzx.com/pc-gpe/texture.txt), Sean Barrett introduces the idea of using planar texture mapping, with a technique using 9 'magic numbers'. Interestingly, these numbers are used to compute three values $a$, $b$, $c$ and finally the texture coordinates are obtained as $u = a / c$ and $v = b / c$. It turns out this computes a ray-plane intersection, and $c$ is the dot product between the plane normal and view ray direction! This can be guessed in the article from the expression of Oc,Hc,Vc that is the cross product of M and N, two vectors defining the texture plane, and hence the texture plane normal. But we don't have to guess, because Sean Barrett wrote [another article detailing this idea](https://nothings.org/gamedev/ray_plane.html)!
218238
219239
The only difference is that I store the precomputed dot product into a table, to avoid the per-pixel division (and that we can now implement this in hardware on an FPGA!).
220240
221241
> Huge thanks to Sean Barrett for [discussions on z-constant and planar texture mapping](https://twitter.com/sylefeb/status/1565425789063020545). Make sure to read his [1994 PCGPE article](http://qzx.com/pc-gpe/texture.txt) that discusses many important aspects of texturing.
222242
243+
### On lightmaps
244+
245+
A big challenge in the `q5k` demo was to support lightmaps: textures which contain light information blended with the standard color textures.
246+
247+
The lightmaps of every polygon in the level are packed into standard textures (this is done by the [qrepack](demos/q5k/qrepack/qrepack.cc) tool). However the key question was how to blend the lightmaps with the color textures?
248+
249+
<center>
250+
<img src="docs/textures.jpg" width="200px"/> <font size=20px>+</font>
251+
<img src="docs/lmaps.jpg" width="200px"/> <font size=20px>=</font>
252+
<img src="docs/both.jpg" width="200px"/>
253+
</center>
254+
255+
Fortunately, the per-column buffers of the DMC-1 (see `colbufs` in [the design](hardware/GPUs/dmc-1/dmc-1.si)) store 16 bits per pixel: 8 bits for a color byte (index in the palette), and an 8-bits light level. That is because I do not use the Doom/Quake palette trick for lighting, but instead dim the actual RGB values after palette lookup, before sending data to the screen.
256+
257+
So to blend the lightmaps in, all that is needed is to write the light information in a second pass, sending each rasterized column span twice: first for color textures, second for lightmaps. The depth test is set to allow writes when depth is equal, so that only the
258+
surfaces that are exactly overlapping the visible ones go through.
259+
223260
### Design walkthrough
224261
225-
I prepared a walkthrough of the [Silice](https://github.com/sylefeb/Silice) design in [this separate page](./docs/DMC-1-walkthrough.md).
262+
I started a walkthrough of the [Silice](https://github.com/sylefeb/Silice) design in [this separate page](./docs/DMC-1-walkthrough.md).
226263
227264
### Discussion
228265
@@ -237,3 +274,5 @@ ___
237274
contains excellent explanations regarding the Comanche terrain rendering algorithm.
238275
- Doom shareware data, `doom1.wad`, is automatically downloaded and is of course
239276
part of the original game by Id Software.
277+
- Quake *e1m1.bsp* used in the q5k demo is downloaded from [this repository](https://github.com/fzwoch/quake_map_source) and uses free textures from the ["Quake
278+
Revitalization Project"](http://qrp.quakeone.com/).

demos/Makefile

+31-11
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ MCH2022_PORT ?= /dev/ttyACM1
44

55
SOC_PATH=../hardware/SOCs/ice40-dmc-1/
66
SOC_ALN_PATH=../hardware/SOCs/ice40-dmc-1-standalone/
7-
SW_PATH =../software/compile/ice40/
7+
HW_COMMON_PATH=../hardware/common/
8+
SW_PATH=../software/compile/ice40/
9+
10+
ifeq ($(DEMO),q5k)
11+
DATA_PACK=quake.img
12+
else
13+
DATA_PACK=textures.img
14+
endif
815

916
$(BOARD) : build/$(BOARD).bin build/$(DEMO).img
1017

11-
simulation : build/$(DEMO).img build/textures.img $(SOC_PATH)/soc-ice40-dmc-1-risc_v.si
18+
simulation : build/$(DATA_PACK) build/$(DEMO).img $(SOC_PATH)/soc-ice40-dmc-1-risc_v.si
1219
mkdir -p build
1320
head -c 1M < /dev/urandom > rnd.raw
1421
cat rnd.raw build/$(DEMO).img > data_tmp.raw
1522
truncate -s 2M data_tmp.raw
16-
cat data_tmp.raw build/textures.img > $(SOC_PATH)/data.raw
23+
cat data_tmp.raw build/$(DATA_PACK) > $(SOC_PATH)/data.raw
1724
rm rnd.raw data_tmp.raw
1825
make -C $(SOC_PATH) -f Makefile verilator
1926

20-
standalone : build/textures.img $(SOC_ALN_PATH)/soc-ice40-dmc-1-standalone.si
27+
standalone : build/$(DATA_PACK) $(SOC_ALN_PATH)/soc-ice40-dmc-1-standalone.si
2128
mkdir -p build
2229
make -C $(SOC_ALN_PATH) -f Makefile $(BOARD)
2330
cp $(SOC_ALN_PATH)/BUILD_$(BOARD)/build.bin build/$(BOARD)_standalone.bin
@@ -36,23 +43,34 @@ build/$(BOARD).bin : $(SOC_PATH)/soc-ice40-dmc-1-risc_v.si
3643
make -C $(SOC_PATH) -f Makefile $(BOARD)
3744
cp $(SOC_PATH)/BUILD_$(BOARD)/build.bin build/$(BOARD).bin
3845

39-
build/$(DEMO).img : $(DEMO)/$(DEMO).c build/level.h
46+
build/$(DEMO).img : $(DEMO)/$(DEMO).c
4047
mkdir -p build
48+
ifneq (,$(wildcard $(DEMO)/Makefile))
49+
make -C $(DEMO)
50+
endif
4151
$(SW_PATH)/compile_c.sh $(DEMO)/$(DEMO).c
4252
make -C $(SW_PATH)
4353
cp $(SW_PATH)/build/code.img build/$(DEMO).img
4454

55+
ifeq ($(DEMO),doomchip-onice)
56+
build/$(DEMO).img : build/level.h
57+
endif
58+
4559
build/textures.img:
4660
cd data ; ./get_data.sh
4761
make -C data ../build/textures.img
4862

63+
build/quake.img:
64+
mkdir -p build
65+
make -C q5k
66+
4967
build/level.h:
5068
cd data ; ./get_data.sh
5169
make -C data ../build/level.h
5270

53-
program_all: build/$(BOARD).bin build/$(DEMO).img build/textures.img
71+
program_all: build/$(DATA_PACK) build/$(BOARD).bin build/$(DEMO).img
5472
ifeq ($(BOARD),icebreaker)
55-
iceprog -o 2M build/textures.img
73+
iceprog -o 2M build/$(DATA_PACK)
5674
iceprog -o 1M build/$(DEMO).img
5775
iceprog build/$(BOARD).bin
5876
else
@@ -62,21 +80,21 @@ ifeq ($(BOARD),mch2022)
6280
python ./build/send.py $(MCH2022_PORT) 1048576 build/$(DEMO).img
6381
./build/webusb_fpga.py build/write.bin
6482
sleep 0.5
65-
python ./build/send.py $(MCH2022_PORT) 2097152 build/textures.img
83+
python ./build/send.py $(MCH2022_PORT) 2097152 build/$(DATA_PACK)
6684
./build/webusb_fpga.py build/$(BOARD).bin
6785
else
6886
echo "no yet implemented"
6987
endif
7088
endif
7189

72-
program_data: build/textures.img
90+
program_data: build/$(DATA_PACK)
7391
ifeq ($(BOARD),icebreaker)
74-
iceprog -o 2M build/textures.img
92+
iceprog -o 2M build/$(DATA_PACK)
7593
else
7694
ifeq ($(BOARD),mch2022)
7795
./build/webusb_fpga.py build/write.bin
7896
sleep 0.5
79-
python ./build/send.py $(MCH2022_PORT) 2097152 build/textures.img
97+
python ./build/send.py $(MCH2022_PORT) 2097152 build/$(DATA_PACK)
8098
else
8199
echo "no yet implemented"
82100
endif
@@ -124,6 +142,8 @@ endif
124142
clean :
125143
rm -rf build
126144
make -C data clean
145+
make -C q5k clean
127146
make -C $(SOC_PATH) -f Makefile clean
128147
make -C $(SOC_ALN_PATH) -f Makefile clean
129148
make -C $(SW_PATH) clean
149+
make -C $(HW_COMMON_PATH) clean

demos/q5k/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.bsp

demos/q5k/Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
all: qrepack/qrepack
2+
./qrepack/qrepack
3+
4+
qrepack/qrepack:
5+
git submodule init
6+
git submodule update
7+
wget -nc https://github.com/fzwoch/quake_map_source/raw/master/bsp/e1m1.bsp
8+
cd qrepack ; cmake . -G "Unix Makefiles" ; make
9+
10+
clean:
11+
rm -f qrepack/qrepack
12+
rm -f qrepack/qrepack.exe
13+
rm -f ../build/quake.img

demos/q5k/emul/CMakeLists.txt

-20
This file was deleted.

demos/q5k/emul/emul_quake.cpp

-62
This file was deleted.

0 commit comments

Comments
 (0)