↓ Skip to main content

Milk-V Jupiter - Half-Life 2 native on RISC-V

·774 words·4 mins
Bas Magré
Author
Bas Magré
Father, happily married, DevOps engineer, C# developer, Python hobbyist, ex Oracle developer. Open-source freak, Opel Kadett C and ‘Hack the planet’ as a hobby.

Intro
#

In my last post I got my AMD Radeon R9 290 working on my Milk-V Jupiter with a mainline kernel. With KDE Plasma and without artifacts. So now what? Games!

Some time ago I played Half-Life 1 on my StarFive VisionFive 2. Half-Life 2 was always on my list. There is a project on GitHub, nillerusr/source-engine, the Source engine that is already ported to ARM and Android. I looked at it before, but it is a huge C++ project from around 2004, with assembly and its own build system. I’m a C# and Python developer, so I gave up.

But now I have claude. So let’s try again!

Half-Life 2 native on RISC-V
Half-Life 2 native on RISC-V

Setup
#

The same setup as with the kernel: a smart-plug (Home Assistant), a USB to serial and an HDMI capture card. So Claude can power the board on and off, read the serial console and see the screen.

First on my laptop
#

Rule one: first make it work on a normal machine. So the first build was on my x86 laptop with the game data of my Steam account.

The build was easy. The game started and… all the text in the menu was white blocks!

white blocks instead of text
white blocks instead of text

Claude checked the fonts, the font library, a software renderer… all fine. The real problem: in 2024 (20th anniversary) Valve updated Half-Life 2 on Steam, also the shaders. This engine code is from 2018, so the new shaders don’t match.

The fix: in Steam choose the beta branch steam_legacy (Properties → Betas). So if you want to try this yourself, use steam_legacy!

Cross-compile for RISC-V
#

I don’t build on the board, it is slow. Claude made a container (Debian 13, same as on the board) that cross-compiles for RISC-V on my laptop. One build takes about 22 minutes.

The code needed some changes:

  • The engine did not know RISC-V (only x86 and ARM), so RISC-V is added.
  • The SSE (Intel) math instructions are translated with SIMDe.
  • Some old x86 assembly is replaced with normal code.
  • On RISC-V a char is unsigned, on x86 it is signed. A compiler flag fixed that.

Copied the game files to the board, started it and… It just worked! Half-Life 2 on RISC-V, around 52 fps on 1280x720. This went faster than the kernel work!

first run on the board
first run on the board

RVV (vector) build
#

The K1 has RVV 1.0 (vector instructions, like SSE/AVX on Intel). That must be faster, right? First it crashed, multiple times:

  • The compiler made vector code of normal loops, but some data is not aligned. The kernel fix for unaligned memory only works for normal instructions, not vector instructions. Fix: -fno-tree-vectorize.
  • The compiler also made vector code of memcpy. The game copies data straight into the memory of the graphics card (over PCI-e), and a vector write to that memory is not allowed on this chip. Crash, and once a full freeze of the board. Again a compiler option fixed it.

And the result? 52 fps. Exactly the same :(

Measuring
#

So Claude made a timedemo benchmark (the same 2 minutes of the game, every run) and built perf for my kernel. What we learned:

  • 640x480 was only 4% faster, so the GPU is not the problem (the GPU was busy only ~9% of the time).
  • The main game thread uses one CPU core for 100%. That is the bottleneck.
  • The OpenGL driver also uses a lot of CPU time…

Vulkan with DXVK
#

…so Vulkan! Half-Life 2 was made for DirectX 9. On Linux this engine translates DirectX 9 to OpenGL (ToGL). DXVK translates DirectX 9 to Vulkan, and there is a native Linux version of it.

It was late, so I gave Claude the task and went to bed. Within about three hours there was a working version, on my laptop and on the board.

Half-Life 2 with DXVK (Vulkan) on the board
Half-Life 2 with DXVK (Vulkan) on the board
Build fps
Board, OpenGL (ToGL) 51-52
Board, RVV build 52
Board, Vulkan (DXVK) 57-59 (+12%)
Laptop, OpenGL 725
Laptop, Vulkan 1004

Not the big jump I hoped for, but it is better!

Video
#

I also made a video of all of this:

Half-Life 2 running natively on RISC-V (YouTube)

GitHub Repo
#

You need your own copy of Half-Life 2 (Steam, steam_legacy). The repo is only the engine, not the game data.

So now what?
#

Create pull requests to upstream, feature by feature: RISC-V support, DXVK and some small fixes.