Simula On NixOS

Time 6 minute read Calendar 2024-10-27 Person George Singer Pricetags #update and #timeline

1 Software Updates

Simula originally started as a pure software project to provide Linux with a VR compositor. Though our compositor is stable, it's development has stalled as we focus our efforts on hardware and shipping the Simula One. With that being said, we recently pushed some new changes for the Simula One release. These updates include:

  1. Adding a (forked) instance of monado as a submodule: Monado provides an implementation of an OpenXR runtime that the Simula One uses to help out with (i) optical distortion correction and (ii) positional & orientation tracking. So far, our forked instance mainly implements driver support for the Xvisio XR50, which is a tracking device we've been using for development. Along this front, Simula now provides simula-monado-service (with all of our proper settings configured), which should be launched before simula itself.

  2. Simula logs and configuration files have been placed in XDG_* folders: We chose to use XDG_* folders for organizing Simula's user data/configurations/logs. These XDG_* folders come from the "XDG Base Directory Specification", which provide standardized locations for user-specific data alongside other Linux/Unix applications. Right now, the most important locations are

    • Simula's Environments ($HOME/.local/share/Simula/environments): Simula's "wallpapers" (or more accurately: skybox textures) are stored here.

    • Simula's Configuration Files $HOME/.config/Simula/*:

      • config.dhall: Provides Simula compositor settings (for e.g. setting default app window sizes, skybox textures, transparency settings, etc) as well as configurable keyboard shortcuts for quick app launching.
      • HUD.config: Provides Simula One HUD settings (for things like showing system clock time, Simula One battery life, resource usage display, etc).

    • Log Data ($HOME/.local/share/Simula/log/*): Simula collects log data into this location (mostly useful for Simula development, or those working to hack or modify our VR compositor).

    • In-Game Screen Capture ($HOME/.local/share/Simula/media/*): Simula includes out-of-box support for in-game screen capture, for both photos (bound to Shift + Printscreen by default) and videos (bound to Super + Shift + Printscreen by default).

  1. Bumped nixpkgs. We bumped our nixpkgs compatibility up to 24.05, which should hold us over until Nix's next biannual update at the end of November.

  2. Fixed build errors associated with impure nix versioning: Building Simula with older/incompatible versions of nix can lead to trouble. To get around this, our nix build script now bootraps into a properly versioned nix (--version 2.18.2, at the moment) which we know will work with everything. We also removed dependencies from foreign git submodules, which have given us trouble in the past.

Overall, we look forward to providing more substantial updates to the compositor next year.

2 Why the Simula One will ship with NixOS

Simula One headsets will come shipped with NixOS as its default OS (with our open-source VR compositor pre-installed as the default window manager). Some advantages of this approach:

  • Functional goodness: We're already pretty familiar with nix tooling, and believe its functional approach to package and system management is a super elegant idea.

  • Package management dominance. Shockingly, nixpkgs seems to be the most dominant package repository in Linux at the moment (in terms of both the number of packages offered, and how often those packages are updated). So it seems like a pretty good foundation to rest upon:

  • System rollbacks: NixOS offers easy system rollbacks if major issues occur, which provides an easy way to push the Simula One back to its most recent working state in case something ever breaks.

  • Enthusiasm: A disproportionate share of Simula fans seem to already use NixOS (roughly ~50% by our unscientific count).

Of course, those who want to use their own Linux distribution (or some other VR window manager other than ours) are free to do so. We're offering an open platform for people to hack/alter in any way they see fit.

3 Simula's NixOS Settings

Simula's NixOS configuration code is pretty simple. First, we pull the desired commit from Simula's GitHub repository, and use it to add simula to the list of system dependencies:

let
  # Fetch Simula source code from GitHub
  simulaSrc = pkgs.fetchFromGitHub {
    owner = "SimulaVR";
    repo = "Simula";
    rev = "7497ada3e415299c88cefed9e58ffec8a654362e";
    sha256 = "04kqlgs740784642fbbypfyv3wa3ddqd9d764wadhjr36mf2rqi6";
    fetchSubmodules = true;
  };

  # Build the Simula package
  simula = pkgs.callPackage "${simulaSrc}/Simula.nix" {
    onNixOS = true;
    devBuild = false;
    profileBuild = false;
    externalSrc = simulaSrc;
  };
in
{
  # Add Simula
  environment.systemPackages = with pkgs; [
     # ...
     simula
     xorg.xrandr
     read-edid
     # ...
  ];

We also ensure that Simula's monado instance has the right setcap capabilities:

# Ensures that tracking devices can set their own priority
security.wrappers = {
  simula-monado-service = {
    source = "${simula}/bin/simula-monado-service";
    capabilities = "cap_sys_nice+ep";
    owner = "simula";
    group = "users";
  };
};

We then provide optical transforms to monado, so that it can distort VR images each frame to display them properly in our headset:

environment.etc."simula/simula_monado_config.json".text = ''
   {
     "display_distortion": {
       "left_eye": {
         "half_fov": 0.9,
         "display_size_mm_x": 51.7752,
         "display_size_mm_y": 51.7752,
         "params_red": {
           "k1": 0.022474564980657766,
           "k3": 6.426774232E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         },
         "params_green": {
           "k1": 0.022474564980657766,
           "k3": 7.140860258225189E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         },
         "params_blue": {
           "k1": 0.022474564980657766,
           "k3": 7.934289176E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         }
       },
       "right_eye": {
         "half_fov": 0.9,
         "display_size_mm_x": 51.7752,
         "display_size_mm_y": 51.7752,
         "params_red": {
           "k1": 0.022474564980657766,
           "k3": 6.426774232E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         },
         "params_green": {
           "k1": 0.022474564980657766,
           "k3": 7.140860258225189E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         },
         "params_blue": {
           "k1": 0.022474564980657766,
           "k3": 7.934289176E-6,
           "k5": 1.557249471935154E-8,
           "k7": 3.456308599003131E-11,
           "k9": -3.9098738307993384E-15
         }
       }
     }
   }
 '';

Finally, we provide a launch script so that monado's OpenXR runtime and simula are launched on Simula One boot:

# Launch monado + Simula's VR window manager on boot
services.xserver = {
  enable = true;
  autorun = true;
  displayManager.lightdm.enable = false;
  displayManager.startx.enable = false;
  displayManager.autoLogin.enable = true;
  displayManager.autoLogin.user = "simula";
  displayManager.defaultSession = "simula-session";
  desktopManager.session = [
    {
      name = "simula-session";
      start = ''
        #!${pkgs.bash}/bin/bash

        # Set up logging
        exec >~/.xsession-errors 2>&1

        cd ~

        echo "Checking for Simula One VR displays..."

        # List available displays, and check if Simula's VR displays are detected
        checkForDisplays() {
          local found_displays=()
          local exit_code=0

          # Load up the `found_displays` array with any detected displays
          for output in /sys/class/drm/*/status; do
            if grep -q "^connected$" "$output"; then
              display_name="''${output%/status}"
              display_name="''${display_name#/sys/class/drm/}"
              found_displays+=("$display_name")

              edid_file="''${output%status}edid"
              if [ -f "$edid_file" ]; then
                edid_model=$(${pkgs.read-edid}/bin/parse-edid < "$edid_file" 2> /dev/null | sed -n 's/.*VendorName *"\(.*\)".*/\1/p')
                if [ -n "$edid_model" ]; then
                  found_displays+=("$edid_model")
                fi
              fi
            fi
          done

          if [ $# -eq 0 ]; then
            echo "Found displays:"
            for display in "''${found_displays[@]}"; do
              echo "  - $display"
            done
            return 0
          fi

          if [[ " ''${found_displays[*]} " == *" $1 "* ]]; then
            echo "$1 display found."
          else
            echo "ERROR: $1 monitor not connected or found."
            exit_code=1
          fi

          return $exit_code
        }

        checkForDisplays
        checkForDisplays "SVR"

        echo "Setting xrandr modes for Simula One displays..."
        ${pkgs.xorg.xrandr}/bin/xrandr --newmode 1280x720_60.00 74.48 1280 1336 1472 1664 720 721 724 746 -HSync +Vsync
        ${pkgs.xorg.xrandr}/bin/xrandr --addmode DVI-I-1-1 1280x720_60.00
        ${pkgs.xorg.xrandr}/bin/xrandr --output DVI-I-1-1 --mode 1280x720_60.00
        ${pkgs.xorg.xrandr}/bin/xrandr --output DP-2 --off
        ${pkgs.xorg.xrandr}/bin/xrandr --output DP-1 --off

        echo "Launching Monado..."
        rm /run/user/$(id -u)/monado_comp_ipc || true
        XRT_NO_STDIN=1 ${simula}/bin/simula-monado-service &

        echo "Waiting for Monado to be ready..."
        until [ -e /run/user/$(id -u)/monado_comp_ipc ]; do sleep 0.1; done

        echo "Launching Simula..."
        exec ${simula}/bin/simula
      '';
    }
  ];
};

3.1 Running a quick test of Simula on NixOS

For those already running NixOS, you can give our software a quick test by adding

let
  # Fetch Simula source code from GitHub
  simulaSrc = pkgs.fetchFromGitHub {
    owner = "SimulaVR";
    repo = "Simula";
    rev = "7497ada3e415299c88cefed9e58ffec8a654362e";
    sha256 = "04kqlgs740784642fbbypfyv3wa3ddqd9d764wadhjr36mf2rqi6";
    fetchSubmodules = true;
  };

  # Build the Simula package
  simula = pkgs.callPackage "${simulaSrc}/Simula.nix" {
    onNixOS = true;
    devBuild = false;
    profileBuild = false;
    externalSrc = simulaSrc;
  };
in
{
  # Provides necessary packages for interacting with Simula launch script
  environment.systemPackages = with pkgs; [
     # ...
     simula
     # ...
  ];

to your configuration.nix. An instance of simula should be added to your system PATH:

$ sudo nixos-rebuild switch
$ simula # press super + z to escape Simula once launched

You don't need a VR headset to run this command; it will simply launch Simula in a new window on your desktop. Once the compositor launches, you can press super + w to engage "WASD movement mode" (which enables you to maneuver around with your mouse + w, a, s, and d keys – like in a first-person shooter -- to move around and launch apps). To escape Simula, press super + z, which will free Simula's control over your mouse and keyboard, allowing you to terminate simula on your machine.