Way of Rhea running natively on Steam Deck

My puzzle game Way of Rhea now runs natively on Linux, and Steam Deck! This is in addition to the existing Windows support.

The new demo also has some quality of life improvements–namely the ability to pause and fast-forward time.

You can give the free demo a try here, if you run into any issues please let me know. :) Please note that if you’ve ever run the game through Proton, Steam will default to Proton despite the presence of a native build. The UI does not always reflect this correctly, you need to toggle Proton on and back off.

The rest of this post covers some technical details for those interested in the porting process, and unfortunately, announces that I’m dropping the planned macOS support.

If you work at Valve and want to review the build so we can be part of Steam Deck Verified, that would be much appreciated! I’m also happy to help you repro the bugs I worked around if they aren’t known issues, shoot me an email.

Table of Contents

Technical info

Why Port to Linux?

Steam Hardware Survey, Windows: 96.2%, Linux: 1.96%, macOS 1.84%

The Steam Hardware Survey is a great resource that (with user consent) reports aggregated information on what hardware/software Steam users are running.

As of the most recent survey, there are more Linux users on Steam than macOS! This is partly due to the release of the Steam Deck.

This is exciting, but, it’s still only 1.96% of users. I decided to port to Linux despite the low percentage:

  • It wasn’t a lot of work, and ideologically I prefer Linux
  • Linux users are over-represented in the group of people that follow my work WRT Rust/Zig/etc
  • Linux is a much more pleasant environment for technical work than Windows
  • And lastly, I think my game is a great fit for the Steam Deck, which runs Linux!

Why not rely on Proton?

Proton is Valve’s fork of Wine. You can use it to run Windows games on Linux–it’s is how the Steam Deck is able to play Windows only games.

My experience with Proton was largely positive, but when players encountered problems I found them hard to debug and hard to work around–you can’t make Proton specific tweaks since it’s pretending to be Windows.

Porting the game to run natively on Linux simplifies both support and development, and has the added benefit of allowing me to compile the game from Linux machines without cross compilation.

To everyone who has sent me crash logs from Proton–thank you! I read every crash log I get, and believe that the native Linux demo has resolved all unresolved crashes.

Windowing

I’m a glutton for punishment, so I wrote custom context creation/windowing/input code for my engine instead of using SDL2 or GLFW like a normal person.

On Windows this means working with win32, on Linux I chose to use libX11 but will probably try Wayland next time.

Windowing code is always a bit of a nightmare, but my experience with X11 was better on average than working with win32. One quick tip for anyone else porting to X11, it took me forever to figure out how to make a window fullscreen because it requires extended window manager hints:

XEvent event = {};
event.type = ClientMessage;
event.xclient.window = window;
event.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", false);
event.xclient.format = 32;
event.xclient.data.l[0] = _NET_WM_STATE_ADD;
event.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false);
XSendEvent(
    display,
    DefaultRootWindow(display),
    false,
    SubstructureNotifyMask | SubstructureRedirectMask,
    &event
);

Graphics card selection

Many computers now have two graphics cards: an integrated card, and a discrete card. The integrated card is good for power saving, the discrete card is good for going fast.

While most new graphics APIs allow you to enumerate the available cards and pick one, I built Way of Rhea on top of OpenGL which lacks this functionality. On Windows you can export some magic symbols to request the fast card but this does not appear to work on Linux.

You can set the environment variables DRI_PRIME and __NV_PRIME_RENDER_OFFLOAD to 1, but this will only work if you’re using the open source Nvidia drives which in my experience are slower than using the integrated card because Nvidia is anticompetitive (signed firmware is required to change the clock rate.)

The proprietary drivers will respect __GLX_VENDOR_LIBRARY_NAME="nvidia", but that’s not an option since not all players will have Nvidia cards.

I sadly had to give up on solving this problem. Way of Rhea will run on the default GPU on Linux unless otherwise configured by the user, I don’t expect this to be a huge support issue since it runs well on integrated cards. In future games I’ll just use the newer graphics APIs.

Steam Deck specific tweaks

Audio problems

At the time of writing, some games have intermittent audio problems on Steam Deck: audio occasionally drops out, or plays at an increased tempo (but not an increased pitch.)

I didn’t want to ship Way of Rhea with audio glitches even if other games have them–the glitches are rare but they break immersion/focus–so I dug into the problem a bit, and was able to determine the sequence of events leading to the glitch:

  1. An underflow occurs
    • An underflow is when you fail to fill the audio buffer in time
    • When programming audio you want to minimize the frequency of these, but you can’t guarantee you’ll never underflow in a game
  2. After the underflow, pulseaudio requests more data on the next write callback
    • Presumably they are trying to play catch up
    • I’ve only see this behavior on Steam Deck
  3. If you honor this request, pulseaudio complains that you overflowed the buffer (bug?)
  4. This repeats for all future frames, and as a result, audio is played with increased tempo
    • More bytes of audio are being consumed per second than should
    • The extra bytes are seemingly thrown out before playback, presumably because they’re considered overflow
    • This is essentially an inadvertent implementation of granular synthesis–a method for implementing tempo changes!

The fix is to call pa_stream_flush(stream, NULL, NULL) from your underflow callback.

(Or at least, that fixes it for me. If you’re a pulseaudio expert and think I should be solving the problem some other way let me know!)

Steam Input problems

The Steam Input UI

Steam Input is a player facing feature that allows players to remap inputs, for example to add controller support to a game that didn’t ship with it.

It’s a great idea on Valve’s part, and it’s why you can play games on Steam Deck that shipped without any controller support whatsoever.

It also has an optional developer facing API that can be used in place of e.g. XInput. Unfortunately this API isn’t super pleasant to work with right now:

  • Its behavior is inconsistent in important ways across platforms
  • Some advanced features are buggy/incomplete/under-documented
  • Updating your default controller layouts involves managing Valve’s custom DSLs, and the parser for their DSLs does not report errors very well
  • The API is missing some obvious safety checks

Developers are free to ignore this API–that’s probably what I’ll do in the future, the player facing features will work regardless–but Way of Rhea relies on it. This section covers some Steam Input related issues I encountered while porting to Steam Deck, and how I worked around them.

Ovelay issues

Way of Rhea triggers the Steam Overlay a few places in game by calling SteamAPI_ISteamInput_ShowBindingPanel/SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage.

Right now, if initiated on button down this operation usually fails on Steam Deck, because the game regains focus on button up. You can work around this by triggering the overlay on button up instead of on button down.

Note that the overlay will also fail to open if you previously returned from a web page in the overlay with “B” instead of by exiting the browser. You can’t work around this AFAIK, I’m just mentioning it because being unaware of it could confound your testing of the above fix.

Action sets

Steam Input’s Action Sets implementation is inconsistent across platforms in a way that I cannot work around:

  1. On most devices, if you switch action sets and then call SteamAPI_ISteamInput_RunFrame, the following frame of data incorrectly reports all actions as inactive (inactive == not available in the active action set)
  2. On the Steam Deck, it also reports all action states as false (state == pressed or not)
  3. As a result, we cannot get consistent behavior across platforms if the same control is assigned to different actions in different action sets

This is not a nitpick–it breaks common patterns like assigning escape to “pause” in the game action set, and to “dismiss” in the menu action set.

The solution is to implement the action set functionality in game instead of relying on Steam. I actually had the code for this already, because I needed it for keyboard input!

Unfortunately, this means that all Steam Input actions need to be stored in a single Steam Input Action Set, which is not always possible because Steam does not allow multiple actions to be bound to the same analog control. e.g. if you bind menu up/menu down to the left stick, you can’t also bind player move to the left stick. This isn’t an issue on digital controls.

There’s not really a nice answer here. I finagled things so that I’d only have one action per analog input in the default binding by re-purposing existing actions where necessary (e.g. instead of “player move” and “ui move” there’s just “move” that controls both.)

Takeaways

While there were a few hiccups, supporting Steam Deck wasn’t a huge undertaking once the game supported Linux. Many games will work out of the box on Steam Deck because of Proton with only minor tweaks to the controls.

Porting to Linux itself was not a lot of work either, and has already made my development environment much pleasanter. I plan to build future games with native Linux support from the start.

Way of Rhea drops plans to support macOS

With sincere apologies to the mac users who were looking forward to playing Way of Rhea, I’m dropping the planned macOS support. This has been a long time coming, and it’s time that I make it official. :(

Why I’m dropping macOS support

I was an Apple user my entire life until a few years ago. However, Apple has done virtually everything within their power to dissuade me from releasing software for their platform:

  1. They deprecated OpenGL, and did not adopt Vulkan
    • OpenGL still works right now, but I can’t accept money for deprecated technology on a platform not known for backwards compatibility
    • I also can’t justify the cost of porting to Metal. Translation layers may be viable, but…
  2. They switched to ARM
    • I have nothing against ARM! But to develop for macOS, I’d need to spend thousands of dollars on a new Mac. Last time I did that all the keys fell off…
    • I’m self funded, so my resources are limited. For the price of a new Mac, I could show my game at PAX and get 10x more wishlists in a week than the total number of Mac users that have wishlisted my game in the past 5 years.
  3. Lastly, Apple now charges developers a subscription fee for the right to make or maintain software for macOS
    • This is inexcusable. It’s inexcusable that they did it on their phones, and it’s inexcusable that they’re bringing the practice to personal computers. I am ideologically at odds with this practice and refuse to participate.

Keep in mind, on top of all this, Apple has no leverage here–they are the minority platform on Steam. At the time of writing Windows users make up 96.2% of Steam users, Linux 1.96%, and macOS 1.84%.

Apple is aware that they have no leverage here, but games like mine don’t affect their bottom line. Thankfully, Apple doesn’t affect my bottom line either.

How can I play Way of Rhea if I’m a Mac user?

Way of Rhea running on a MacBook via Bootcamp Way of Rhea running on a MacBook via Bootcamp.

If you’re a Mac user, and you still want to play Way of Rhea despite the lack of support, here are some options. I apologize that it’s not as easy as just downloading it and pressing play:

  • Dual boot macOS with Windows or Linux.
  • Try running Wine–if you get this working let me know and I’ll update this post to mention that it’s been done!
  • Purchase a cheap computer and run Windows or Linux on it. Way of Rhea is not particularly performance intensive.
  • Purchase a Steam Deck. They’re cheap, and run Way of Rhea (and a bunch of other games that your Mac won’t support) great.
  • Purchase a gaming computer. If you’re used to paying for Macs, gaming computers may be more affordable than you think, and once you get one you’ll be able to run pretty much any game on Steam.