Trying to make SimulaVR work with HTC Vive

I’ve purchased a used HTC Vive and I’ve been trying, for the past few days, to make SimulaVR work with it, using Copilot with Claude Sonnet 4 to help me make it work, as I don’t have a lot of free time to delve into how it works.

First I tried to install it on my Debian. Because SimulaVR was built with the NixOS in mind, I had to install the nix package manager and work through it. I faced several issues and came to the conclusion it would be best to attempt on a proper NixOS.

I’ve found an old 160 GB HD (not SSD) and I had a SATA to USB adapter so I installed NixOS in it. I’m using 25.11.8107.1073dad219cb-x86_64-linux. Not LTS, because I wanted to ensure I would have any package I might need.

I’ve installed SimulaVR in it. Because of some issues with their binary caches, I had to compile a lot of stuff. This was all done using the nix package manager, not make etc. I had to add some swap memory to do it.

After several days of back-and-forth with Copilot, I’ve managed to make Monado and Simula run, but I can’t get SimulaVR to run in the Vive. Copilot’s consensus seems to be that, while Monado starts in direct mode, Simula starts in X11 and they have trouble sharing contexts.

The best result I had was the following: Monado starts, the Vive turns on and displays a blank white screen. Simula starts, opens a window with a cloudy sky background and a terminal inside, which I can interact with, but I only see this on the laptop. On most other tests, either Simula crashes if Monado is up or starts in desktop mode only if Monado is down (commands used and output captured are attached at the end).

I got to the point where Copilot started suggesting me to

  1. Edit ~/.config/monado/config_v0.toml to disable direct mode and etc.
  2. Create udev rules to force Monado to use X11
  3. Suggested to use WiVRn as an alternative OpenXR Runtime
  4. Using the AMD integrated graphics card instead of the nVidia GPU
  5. Using an LLVMpipe software renderer
  6. “Creating a custom Vulkan ICD that combines both drivers”
  7. Check for existing issues…

Now I strongly suspect that I shouldn’t be messing with such low-level stuff and that the solution is likely a lot simpler.

I’ve opened an issue in the SimulaVR repository and this is the reply that I got:

Sorry about this. @KaneTW and I haven’t used Simula with an HTV Vive in many years, so we can’t guarantee our fork of monado (simula-monado-service) will support it.

[…]

So it seems that it would not make sense to continue nagging them about it. They suggested me to attempt to use the original Monado, so I proceeded to do that.

Now I had to mess with make because Monado doesn’t have pre-compiled binaries I could use. Another several days with Copilot later, I managed to compile Monado and run it. However, this time, the Vive doesn’t turn on. Also, I get some different results out of attempting to run Simula.

Now Copilot was suggesting that either:

  1. Monado and Simula were running on separate OpenXR processes and should be ran in one integrated process
  2. Simula should launch Monado on its own

I think that I’ve reached the limit of how much Copilot can help me. So now my only hope is to reach out for people who are more capable than me in handling this kind of stuff.

Oh yeah, the Vive works just fine on Windows.

Does someone knows how can I make this work? If more information is needed, just ask and I’ll reply as soon as I can.

Thanks in advance.

Here are the logs using simula-monado-service:

__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia DRI_PRIME=1 simula-monado-service

log at Pastebin

(i’ve removed a lot of duplicate Warning: Could not find disambiguator state for HMD)

sudo -u vr DRI_PRIME=1 __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia CAP_SYS_NICE=ep PATH=/run/current-system/sw/bin:$PATH simula

log at Pastebin

(I closed it with Super+Shift+Esc after I confirmed it was “working” in the laptop only)

Also, Monado only starts successfully once per login…

If I try to start Monado without the things suggested by Copilot, I get this:

simula-monado-service

log at Pastebin

And here are the logs using the Monado I compiled myself:

export LD_LIBRARY_PATH="/nix/store/cbifw447vv93ifvv1prqq9nrzi13jhwq-libjpeg-turbo-3.1.2/lib:/nix/store/2srsqvz4ghjcnn9xp7vs69iq3jrpz7f1-libtiff-4.7.1/lib:/nix/store/6lavn0jjgm8qmqpbam9bxg8r79inhbyi-libbsd-0.12.2/lib:/nix/store/bigkpra9jw48fip69q4wndsf4kb3d2w9-systemd-258.3/lib:/nix/store/fvp9ndylwa5z5ddvi6nns84q4r5mb8lz-hidapi-0.15.0/lib:/nix/store/3j0jwcygamiza2n1ga69sdk6skaz5k3k-zlib-1.3.2/lib:/home/vr/.local/lib64:/nix/store/vghr3ahlzfy9ji898lf1473yrnyyzv12-opencv-4.12.0/lib:/nix/store/db1178x260jvgvy94hb27xp395rr58fd-sdl2-compat-2.32.60/lib:/nix/store/08bph9k5m4xi8fsj9r057z7jbl18sk4-cjson-1.7.19/lib:/run/current-system/sw/lib/:$LD_LIBRARY_PATH"

export XR_RUNTIME_JSON=~/.local/share/openxr/1/openxr_monado.json

~/.local/bin/monado-service

log at Pastebin

(that Client 1 disconnected happened immediately after Client 1 connected, while Simula was still up and running)

export XR_RUNTIME_JSON=~/.local/share/openxr/1/openxr_monado.json
# export IPC_IGNORE_VERSION=1 # another Copilot suggestion, faired worse

simula

log at Pastebin

Here’s my /etc/nixos/configuration.nix:

{ config, pkgs, ... }:

{
  imports = [ ./hardware-configuration.nix ];

  # Boot configuration
  boot.loader.systemd-boot.enable = true;
  boot.loader.efi.canTouchEfiVariables = true;

  # Enable experimental features for SimulaVR
  nix.settings.experimental-features = [ "nix-command" "flakes" ];

  # Networking
  networking.hostName = "simulavr-nixos";
  networking.networkmanager.enable = true;

  # Enable NVIDIA drivers with Wayland support
  services.xserver.videoDrivers = [ "nvidia" ];
  hardware.nvidia = {
    modesetting.enable = true;
    powerManagement.enable = false;
    powerManagement.finegrained = false;
    open = false;
    nvidiaSettings = true;
    package = config.boot.kernelPackages.nvidiaPackages.stable;
  };

  # Consolidated graphics configuration
  hardware.graphics = {
    enable = true;
    enable32Bit = true;
    extraPackages = with pkgs; [
      mesa
      vulkan-loader
      vulkan-validation-layers
      vulkan-extension-layer
      vulkan-headers
      vulkan-tools
      libdrm
      mesa_glu
      freeglut
      wayland
      wayland-protocols
    ];
    extraPackages32 = with pkgs.pkgsi686Linux; [
      mesa
      vulkan-loader
    ];
  };

  services.xserver = {
    enable = true;
    xkb = {
      layout = "br";
      variant = "abnt2";
    };
  };

  services.displayManager.gdm = {
    enable = true;
    wayland = true;
  };
  services.desktopManager.gnome.enable = true;

  console = {
    keyMap = "br-abnt2";
  };

  i18n = {
    defaultLocale = "en_US.UTF-8";
    extraLocaleSettings = {
      LC_TIME = "pt_BR.UTF8";
    };
  };

  hardware.steam-hardware.enable = true;
  services.udev.extraRules = ''
  # HTC Vive headset
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", MODE="0664", GROUP="plugdev"
    KERNEL=="hidraw*", ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", MODE="0664", GROUP="plugdev"
    
    # Valve/Steam devices (base stations, controllers)
    SUBSYSTEM=="usb", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2000", MODE="0664", GROUP="plugdev"
    SUBSYSTEM=="usb", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2001", MODE="0664", GROUP="plugdev" 
    SUBSYSTEM=="usb", ATTRS{idVendor}=="28de", ATTRS{idProduct}=="2012", MODE="0664", GROUP="plugdev"
    KERNEL=="hidraw*", ATTRS{idVendor}=="28de", MODE="0664", GROUP="plugdev"
    
    # General HTC devices
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bb4", MODE="0664", GROUP="plugdev"
    KERNEL=="hidraw*", ATTRS{idVendor}=="0bb4", MODE="0664", GROUP="plugdev"
    
    # Oculus devices (for compatibility)
    SUBSYSTEM=="usb", ATTRS{idVendor}=="2833", MODE="0664", GROUP="plugdev"
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0fcf", MODE="0664", GROUP="plugdev"
    
    # DRM access
    SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", ENV{ID_SEAT}="seat0"
    SUBSYSTEM=="drm", KERNEL=="controlD[0-9]*", TAG+="uaccess"
    
    # VR specific settings
    ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", ENV{VR_DIRECT_MODE}="false"
    SUBSYSTEM=="usb", ATTRS{idVendor}=="0bb4", ATTRS{idProduct}=="2c87", ENV{XRT_COMPOSITOR_FORCE_WINDOWED}="1"
  '';

  security.rtkit.enable = true;
  services.pipewire = {
    enable = true;
    alsa.enable = true;
    alsa.support32Bit = true;
    pulse.enable = true;
  };

  users.groups.plugdev = {};

  users.users.vr = {
    isNormalUser = true;
    description = "VR User";
    extraGroups = [ "networkmanager" "wheel" "plugdev" "audio" "video" "render" "input" ];
  };

  environment.etc = {
    "xdg/autostart/terminal.desktop" = {
      text = ''
        [Desktop Entry]
        Type=Application
        Name=Terminal
        Exec=kgx
        Hidden=false
        NoDisplay=false
        X-GNOME-Autostart-enabled=true
      '';
    };
    
    "xdg/autostart/vscode.desktop" = {
      text = ''
        [Desktop Entry]
        Type=Application
        Name=VSCode
        Exec=code
        Hidden=false
        NoDisplay=false
        X-GNOME-Autostart-enabled=true
      '';
    };
    
    "xdg/autostart/chrome.desktop" = {
      text = ''
        [Desktop Entry]
        Type=Application
        Name=Chrome
        Exec=google-chrome-stable
        Hidden=false
        NoDisplay=false
        X-GNOME-Autostart-enabled=true
      '';
    };
  };

  # Complete dependency list - added libmd for BSD compatibility
  environment.systemPackages = with pkgs; [
    # VR essentials
    vulkan-tools
    vulkan-loader
    vulkan-validation-layers
    
    # Monado core dependencies
    cmake
    python3
    python3.pkgs.pip
    python3.pkgs.numpy
    python3.pkgs.scipy
    vulkan-headers
    libGL
    libGL.dev
    mesa
    eigen
    glslang
    libusb1
    libusb1.dev
    systemd
    systemd.dev
    libv4l
    libv4l.dev
    
    # Critical missing dependencies
    libcap
    libcap.dev
    libsysprof-capture
    pcre2
    pcre2.dev
    libunwind
    libunwind.dev
    elfutils
    elfutils.dev
    zstd
    zstd.dev
    xz
    xz.dev
    
    # libsurvive specific dependencies
    zlib
    zlib.dev
    libpcap
    openblas
    lapack
    
    # X11 complete for libsurvive
    xorg.libX11
    xorg.libX11.dev
    xorg.libxcb
    xorg.libxcb.dev
    xorg.xcbutil
    xorg.xcbutilwm
    xorg.xcbutilkeysyms
    xorg.libXext
    xorg.libXext.dev
    xorg.libXrandr
    xorg.libXrandr.dev
    xorg.xorgproto
    xorg.libXfixes
    xorg.libXfixes.dev
    xorg.libXau
    xorg.libXau.dev
    xorg.libXdmcp
    xorg.libXdmcp.dev
    xorg.libXrender
    xorg.libXrender.dev
    libGLU
    libdrm
    libdrm.dev
    
    # Media libraries
    libjpeg_original
    libjpeg_original.dev
    libjpeg_turbo
    libjpeg_turbo.dev
    libjpeg8
    libjpeg8.dev
    mozjpeg
    opencv
    libuvc
    
    # OpenHMD and HIDAPI
    openhmd
    hidapi
    
    # Bluetooth stack
    bluez
    bluez.dev
    
    # Wayland complete
    wayland
    wayland.dev
    wayland-protocols
    wayland-scanner
    libffi
    libffi.dev
    
    # GLib
    glib
    glib.dev
    
    # SDL2
    SDL2
    SDL2.dev
    
    # Additional utilities
    doxygen
    graphviz
    cjson
    
    # EGL support
    libglvnd
    libglvnd.dev
    
    # GStreamer complete with ORC
    gst_all_1.gstreamer
    gst_all_1.gst-plugins-base
    gst_all_1.gst-plugins-good
    gst_all_1.gst-plugins-bad
    gst_all_1.gst-plugins-ugly
    orc
    
    # D-Bus
    dbus
    dbus.dev
    
    # BSD utilities - ADDED libmd for BSD compatibility
    libbsd
    libbsd.dev
    libmd                                    # Added missing libmd dependency
    
    # OpenVR
    openvr
    
    # Build tools
    git
    ninja
    meson
    pkg-config
    stdenv.cc
    gcc
    gnumake
    glibc.dev
    
    # System utilities
    nano
    wget
    htop
    pciutils
    linuxPackages.nvidia_x11
    usbutils
    xorg.xrdb
    wmctrl
    i3status
    firefox
    xorg.xhost
    vscode
    google-chrome
  ];

  # Enhanced environment variables - added libmd paths
  environment.variables = {
    PKG_CONFIG_PATH = pkgs.lib.concatStringsSep ":" [
      "${pkgs.systemd.dev}/lib/pkgconfig"
      "${pkgs.libusb1.dev}/lib/pkgconfig" 
      "${pkgs.bluez.dev}/lib/pkgconfig"
      "${pkgs.libv4l.dev}/lib/pkgconfig"
      "${pkgs.xorg.libX11.dev}/lib/pkgconfig"
      "${pkgs.xorg.libXext.dev}/lib/pkgconfig"
      "${pkgs.xorg.libXrandr.dev}/lib/pkgconfig"
      "${pkgs.xorg.libxcb.dev}/lib/pkgconfig"
      "${pkgs.xorg.libXfixes.dev}/lib/pkgconfig"
      "${pkgs.xorg.xorgproto}/lib/pkgconfig"
      "${pkgs.xorg.libXau.dev}/lib/pkgconfig"
      "${pkgs.xorg.libXdmcp.dev}/lib/pkgconfig"
      "${pkgs.xorg.libXrender.dev}/lib/pkgconfig"
      "${pkgs.libGL.dev}/lib/pkgconfig"
      "${pkgs.libdrm.dev}/lib/pkgconfig"
      "${pkgs.wayland.dev}/lib/pkgconfig"
      "${pkgs.SDL2.dev}/lib/pkgconfig"
      "${pkgs.zlib.dev}/lib/pkgconfig"
      "${pkgs.libjpeg_original.dev}/lib/pkgconfig"
      "${pkgs.libjpeg_turbo.dev}/lib/pkgconfig"
      "${pkgs.libjpeg8.dev}/lib/pkgconfig"
      "${pkgs.libglvnd.dev}/lib/pkgconfig"
      "${pkgs.libffi.dev}/lib/pkgconfig"
      "${pkgs.dbus.dev}/lib/pkgconfig"
      "${pkgs.libbsd.dev}/lib/pkgconfig"
      "${pkgs.glib.dev}/lib/pkgconfig"
      "${pkgs.libcap.dev}/lib/pkgconfig"
      "${pkgs.libsysprof-capture}/lib/pkgconfig"
      "${pkgs.pcre2.dev}/lib/pkgconfig"
      "${pkgs.libunwind.dev}/lib/pkgconfig"
      "${pkgs.elfutils.dev}/lib/pkgconfig"
      "${pkgs.zstd.dev}/lib/pkgconfig"
      "${pkgs.xz.dev}/lib/pkgconfig"
      "${pkgs.orc}/lib/pkgconfig"
      "${pkgs.libuvc}/lib/pkgconfig"
      "${pkgs.gst_all_1.gstreamer.dev}/lib/pkgconfig"
      "${pkgs.gst_all_1.gst-plugins-base.dev}/lib/pkgconfig"
    ];
    
    CMAKE_PREFIX_PATH = pkgs.lib.concatStringsSep ":" [
      "${pkgs.systemd.dev}"
      "${pkgs.libusb1.dev}"
      "${pkgs.vulkan-headers}"
      "${pkgs.xorg.libX11.dev}"
      "${pkgs.xorg.libxcb.dev}"
      "${pkgs.xorg.libXfixes.dev}"
      "${pkgs.xorg.xorgproto}"
      "${pkgs.xorg.libXau.dev}"
      "${pkgs.xorg.libXdmcp.dev}"
      "${pkgs.xorg.libXrender.dev}"
      "${pkgs.libGL.dev}"
      "${pkgs.wayland.dev}"
      "${pkgs.SDL2.dev}"
      "${pkgs.libglvnd.dev}"
      "${pkgs.glib.dev}"
      "${pkgs.libcap.dev}"
      "${pkgs.pcre2.dev}"
      "${pkgs.libunwind.dev}"
      "${pkgs.elfutils.dev}"
      "${pkgs.zstd.dev}"
      "${pkgs.xz.dev}"
      "${pkgs.orc}"
      "${pkgs.zlib.dev}"
      "${pkgs.libjpeg_original.dev}"
      "${pkgs.bluez.dev}"
      "${pkgs.eigen}"
      "${pkgs.openblas}"
      "${pkgs.lapack}"
      "${pkgs.libpcap}"
      "${pkgs.libmd}"                        # Added libmd to CMAKE_PREFIX_PATH
    ];
    
    # libsurvive specific paths
    EIGEN3_ROOT = "${pkgs.eigen}";
    EIGEN3_INCLUDE_DIR = "${pkgs.eigen}/include/eigen3";
    OPENBLAS_ROOT = "${pkgs.openblas}";
    LAPACK_ROOT = "${pkgs.lapack}";
    LIBUSB_ROOT = "${pkgs.libusb1}";
    LIBUSB_INCLUDE_DIR = "${pkgs.libusb1.dev}/include/libusb-1.0";
    LIBUSB_LIBRARY = "${pkgs.libusb1}/lib/libusb-1.0.so";
    ZLIB_ROOT = "${pkgs.zlib}";
    ZLIB_INCLUDE_DIR = "${pkgs.zlib.dev}/include";
    ZLIB_LIBRARY = "${pkgs.zlib}/lib/libz.so";
    X11_ROOT = "${pkgs.xorg.libX11}";
    X11_X11_INCLUDE_PATH = "${pkgs.xorg.libX11.dev}/include";
    X11_X11_LIB = "${pkgs.xorg.libX11}/lib/libX11.so";
    
    # Other CMake hints
    HIDAPI_ROOT = "${pkgs.hidapi}";
    OpenHMD_ROOT = "${pkgs.openhmd}";
    EGL_ROOT = "${pkgs.libglvnd}";
    OpenCV_ROOT = "${pkgs.opencv}";
    OpenVR_ROOT = "${pkgs.openvr}";
    bluetooth_ROOT = "${pkgs.bluez}";
    
    # JPEG forcing
    JPEG_ROOT = "${pkgs.libjpeg_turbo}";
    JPEG_INCLUDE_DIR = "${pkgs.libjpeg_turbo.dev}/include";
    JPEG_LIBRARY = "${pkgs.libjpeg_turbo}/lib/libjpeg.so";
  };

  nixpkgs.config.allowUnfree = true;
  system.stateVersion = "23.11";

  security.wrappers = {
    "simula-monado-service" = {
      source = "/home/vr/.nix-profile/bin/simula-monado-service";
      capabilities = "cap_sys_nice+ep";
      owner = "root";
      group = "root";
    };
  };
}

and my /etc/nixos/hardware-configuration.nix

# Do not modify this file!  It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations.  Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:

{
  imports =
    [ (modulesPath + "/installer/scan/not-detected.nix")
    ];

  boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usb_storage" "uas" "sd_mod" ];
  boot.initrd.kernelModules = [ ];
  boot.kernelModules = [ "kvm-amd" ];
  boot.extraModulePackages = [ ];

  fileSystems."/" =
    { device = "/dev/disk/by-uuid/7f49efe8-d667-473e-8f73-d6f10a76733c";
      fsType = "ext4";
    };

  fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/5F71-EC96";
      fsType = "vfat";
      options = [ "fmask=0022" "dmask=0022" ];
    };

  swapDevices = [ ];

  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
  hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

I’m using an Acer Nitro 5 with AMD Ryzen 7-5800H and nVidia GTX 1650 (N20C1, AN515-45-R4S3).

Thanks again.