Gavin's Site

I switched to Linux on all of my machines a few years ago, but the announcement of the Steam Frame got me interested in playing VR again. This page details what I needed to get things working. Importantly, I'm also going to include the specific reason for each setting/software used, so future readers can identify if it's still needed.

I don't intend for this page to be the ultimate resource for VR on Linux, you should refer to the Linux VR Adventures Wiki for that. Many of the fixes I had to use were already documented there.

If you learn nothing else from this, please don't make the same mistakes I did and read the Linux VR Adventures Wiki first! Read every page that is relevant to your system and games, top-to-bottom. I would've saved myself much frustration had I done that sooner.

General Setup

I have Steam installed via programs.steam.enable, but I have a separate user account for video games (that is not an admin account). I use KDE 6 on Wayland, with fairly little customization to the base desktop environment. I also don't use Home Manager, so there are a few workarounds that I use to make up for some of that functionality.

I initially got this working with an RTX 2080 with the open Nvidia drivers, but have since upgraded to an AMD RX 9070 XT. The experience in terms of stability is about the same between the two of them, though was generally slightly more stable on Nvidia (unless it involved suspend, in which case all bets are off). That being said, the debugability of AMD is far superior. When something goes wrong, I feel like I have a hope of fixing it rather than just patiently waiting for an update.

Since my experience has been spread across these two different GPUs, I will note where I only have experience with one or the other.

My headset is a Valve Index with the Knuckles controllers, but the V1 base stations (the ones that came with the HTC Vive).

I started with simply installing SteamVR without any additional configurations outside of programs.steam.enable. To my surprise, this mostly worked (with caveats detailed below). There was nothing weird required for room setup or connections, however I had used my headset + controllers on Windows prior, so I didn't have to do any connection tinkering. I was able to play lighter games like Beat Saber without issue, and H3VR worked well.

I would've preferred to stick with SteamVR, but asynchronous reprojection does not work. When you start SteamVR, it tells you that you need admin access to finish setup. This is needed to set the capabilities of the SteamVR compositor binary. However, because NixOS packages Steam under bwrap, it will never succeed. You can manually enter the setcap command to make SteamVR think that it is properly set up, but bubblewrap will still override the executable capabilities at runtime. The NixOS wiki suggests removing this functionality from bubblewrap with a patch, but I personally wasn't comfortable with the security implementations of doing that since it would apply to everything on Steam, and it seemed potentially unreliable.1

I spent some time playing around with the idea of a program to launch the compositor outside the bwrap container through IPC, but after a while I realized that it would be way too much work (and possibly impossible).

While I didn't notice the lack of asynchronous reprojection on Beat Saber or H3VR, I absolutely noticed it on VRChat. VRChat is infamously poorly optimized, and it has its own section below, but it was clear that SteamVR on NixOS was not an option for VRChat.

As a side note, I noticed a lot of failed calls to lsusb by SteamVR in its logs, so I added usbutils to programs.steam.extraPackages. This did not appear to actually fix anything, as the calls still failed, but the failed calls did not appear to cause any problems anyhow.

1

I also feel like I saw somewhere that Steam has become more active in its attempts to remove capabilities that it "shouldn't" have. I could not find where I read this, however.

I was considering abandoning NixOS for gaming when it was suggested I use Monado instead of SteamVR, which apparently had better performace than SteamVR in general.

Monado can by adding the following to your configuration.nix (this is also on the NixOS wiki):

{
  # ...

  services.monado = {
    enable = true;

    # sets cap_sys_nice which is needed for reprojection. This is the default,
    # but I have it explicitly highlighted. I read somewhere that doing so was
    # not enough, and that you needed to use renice on the running process, but 
    # I did not find that to be needed.
    highPriority = true;

    # sets monado as the default OpenXR runtime
    defaultRuntime = true;

    # overwrites the runtime whenever monado is started. Needed because SteamVR
    # likes to set itself as the default runtime (we still need to use SteamVR 
    # occasionally, so this is helpful)
    forceDefaultRuntime = true;
  };

  systemd.user.services.monado.environment = {
    # Uses SteamVR's lighthouse tracking - I believe this means SteamVR must be
    # installed and configured, but I have not verified it. I think there are 
    # FOSS alternatives, but I have not looked into them. This setting caused
    # issues detailed below.
    STEAMVR_LH_ENABLE = "1";
  };
}

Enabling STEAMVR_LH_ENABLE caused monado to segfault whenever it was started due to using a SteamVR API incorrectly (it was changed in recent releases), so I switched to using the latest git version. Preventing this means keeping SteamVR and Monado in sync, which can be challenging due to their different packaging. I disabled autoupdates for SteamVR and I try to avoid launching it2. If nothing is broken, I also try to keep the Monado version unchanged. However, if Monado starts crashing at startup, try the following:

  1. Update both Monado and SteamVR to their latest versions
  2. If it's still broken, use Steam's betas feature to select the "previous" version
  3. Make sure everything is plugged in, for real this time
  4. Realize this time it probably isn't SteamVR's fault

Monado appears to be one of software projects where the released versions are often out-of-date to the point of uselessness. There's no harm in using the latest git.

Note that at time of writing, Monado doesn't support hotplugging of devices. This means that you have to ensure that controllers, base stations, and headset are plugged in and connected before starting Monado. In some cases (e.g. the headset's DP cable isn't plugged in), it will just segfault on launch.

To start Monado3, use:

systemctl --user start monado.service

By default, it's set up to autostart when something tries to connect to it, so I rarely start it manually.

Since it's a systemd service, you can view its logs with:

journalctl --user --follow -p7

I like seeing all the other logs as well, so I don't limit journalctl to just Monado, which can be done with by adding --unit monado.service.

I've also found that Monado doesn't respond to normal attempts to stop it. To stop it, I've had to send SIGKILL.

systemctl --user kill monado.service --signal 9

Allowing Steam to use Monado requires adding the ipc path in the environment:

{
  # ...

  programs.steam = {
      package = pkgs.steam.override {
	  extraProfile = ''
	      # Expose Monado to steam runtimes
	      export PRESSURE_VESSEL_FILESYSTEMS_RW=$XDG_RUNTIME_DIR/monado_comp_ipc

	      # Prefix the launch options for openvr-only games with:
	      # VR_OVERRIDE=$OPENCOMPOSITE_PATH
	      export OPENCOMPOSITE_PATH="${pkgs.opencomposite}/lib/opencomposite"
	      export XRIZER_PATH="${pkgs.xrizer}/lib/xrizer"
	  '';
      };
  };
}

For OpenXR games running in Proton, you need to add: PRESSURE_VESSEL_IMPORT_OPENXR_1_RUNTIMES=1

When I was using Nvidia, I also had to add nvidia-modeset.conceal_vrr_caps=1 to boot.kernelParams to fix the severe screen tearing.

If Audio doesn't work right away, it's because audio is handled by modifying the GPU's output profiles rather than adding a new output device. For me, this meant having to select different profiles for the TU104 HD Audio Controller device in KDE's sound settings. It doesn't appear to be consistent which profile corresponds to the headset, so I occasionally need to go through the profiles and test each one until I find the right one.

2

There are more sophisticated ways of pinning versions on Steam, but I've found this to be largely sufficient.

3

monado.service sometimes seems to start automatically for all users, but because hotplugging isn't supported, I have to restart it. On my non-gaming account, I have it disabled (which I had to do manually).

WayVR, AKA The Software Formerly Known as WlxOverlay-S, is awesome. It has acted as my replacement for OVR Toolkit.

You should look at the github README for an introduction, but it is high-quality software. It includes A VR Wayland compositor, which I think is just about the coolest thing ever.

My initial setup for it involved modifying the example watch config to add a view for nvtop, but I haven't customized it since the metamorphosis into WayVR wiped my config. That isn't to say it's too cumbersome, but rather that the defaults have been good enough that I don't feel the need to.

I launch WayVR through a script right now, but I think it might be nice to make it into a systemd service that launches with Monado?

Nixpkgs XR overlay

I use the nixpkgs community overlay nixpkgs-xr to manage versions of much of the VR related software. I'm a tad too distrustful to blindly use a community overlay like that, so I keep the version pinned and only update it when I manually audit it.

{ config, pkgs, ... }:

let 
  nixpkgs-xr = import (builtins.fetchTarball "https://github.com/nix-community/nixpkgs-xr/archive/1ecf2d20a9ab1b02fdbe53c10546fb460ba80a87.tar.gz");
in
{
  nixpkgs.overlays = [ nixpkgs-xr.overlays.default ];

  # ...
}

The other downside of using an overlay like this is that it makes nixos-rebuild considerably slower it often has to rebuild a bunch of Rust programs.

VRChat was and is the most challenging VR game to get working, mostly because of its horrible performance (I wonder if they would be interested in hiring me as a performance engineer).

As I mentioned above, SteamVR was completely unworkable for me. Right now, I am using the following launch options that I've found to give the best experience. Some I copied from posts on the ProtonDB page without testing them specifically. These are labeled as such. VRChat is particularly difficult to measure the performance of, so it's difficult to know which settings have an impact.

Launch environment variables:

  • PROTON_MEDIA_USE_GST=1 - use winegstreamer as the video backend. This was apparently an issue, but I haven't tested without it.
  • PROTON_USE_NTSYNC=1 PROTON_NO_ESYNC=1 PROTON_NO_FSYNC=1 - use ntsync synchronization. I don't know if the NO_ESYNC and NO_FSYNC is necessary. Expanded on below.
  • I don't have the import openxr runtimes var set because that's handled by services.monado in configuration.nix.

Launch arguments (comes after %command%):

  • --enable-hw-video-decoding - forces hardware video encoding. According to the documentation it's disabled by default on AMD.
  • --enable-avpro-in-proton - needed for some video players. The default of whether it's enabled or not keeps changing, so I'm keeping it on for now.
  • screen-width 640 -screen-height 480 -screen-fullscreen 1 - only affects desktop display. I expect this improves performance, but I haven't measured it.

I found using ntsync to be absolutely essential for performance. I have not specifically tested the other synchronization methods, so I don't know which one is the default on my system. I initially noticed high CPU usage from wineserver, and when I enabled WINEDEBUG I got gigabytes per second of log output that appeared to be IPC. The specific symptoms were strong indicators of synchronization slowness that are too detailed to write here. Adding the ntsync kernel module fixed that (and as a bonus fixed the audio issues that were present as well).

The LVRA wiki recommends using the Proton RTSP patch, but I've had no issues with just using Proton GE, as enabled by adding the following to configuration.nix and selecting Proton-GE in the compatibility settings for VRChat in your Steam library.

{
  # ...
  programs.steam = {
    # ...

    extraCompatPackages = [ pkgs.proton-ge-bin ];
  };
}

VRChat was also the one game where I found using Xrizer over OpenComposite to be important. On OpenComposite, hand gestures involving the thumb would not register (thumb movement would appear, but the specific input for activating the expression was never triggered).

Hot Dogs, Horseshoes & Hand Grenades

Hot Dogs, Horseshoes & Hand Grenades (known as H3VR) generally works well under Monado. It has issues with headset positioning (obvious with night vision) that are fixed with OXR_PARALLEL_VIEWS=14.

4

Xrizer has implemented a workaround for Unity VR games that automatically sets OXR_PARALLEL_VIEWS. I have not tried H3VR under Xrizer yet.

OpenVR (Xrizer and OpenComposite)

TODO (short version: use OpenComposite unless you have reason to use Xrizer. You have to set the runtime path in $XDG_CONFIG_HOME/openvr/openvrpaths.vrpath and make the file read-only)

Other resources

As I stated in the introduction, a lot of this article is restating what was already in the Linux VR Adventures Wiki, as I found they were needed the hard way.

Other than that, here are some other resources I came across in my Linux VR Adventures:

  • NixOS wiki VR page
  • Xe Iaso's My VR Hell on NixOS - a little outdated, more of a cry for help than anything else, and more evidence that when you get good enough at touching computers everything gets harder and nothing works.