From e8a41fc629fd48e5455e6c58331a4b3156ef8398 Mon Sep 17 00:00:00 2001 From: plyshka Date: Sun, 7 Jul 2024 20:26:32 +0500 Subject: [PATCH 1/8] Update outdated wiki entries, small refactors allover wiki, reformats --- .../src/dashboard/components/setup_wizard.rs | 2 +- ...lient-and-streamer-on-separate-networks.md | 96 +++++++-------- wiki/ALVR-in-distrobox.md | 112 ----------------- wiki/Building-From-Source.md | 28 ++--- ...uration-Information-and-Recommendations.md | 77 +++++------- wiki/Controller-latency.md | 2 + wiki/Flatpak.md | 42 ++----- wiki/Hardware-Video-Encoding.md | 109 +++++++++-------- wiki/Home.md | 2 +- wiki/Installation-guide.md | 114 +++++++----------- wiki/Linux-Support-development-progress.md | 92 -------------- wiki/Linux-Troubleshooting.md | 33 +++++ .../My-game-is-not-working-properly!-Help!.md | 2 +- wiki/Real-time-video-upscaling-experiments.md | 14 +-- wiki/Roadmap.md | 2 +- wiki/Settings-tutorial.md | 10 +- wiki/Troubleshooting.md | 10 +- wiki/Use-ALVR-through-a-USB-connection.md | 10 +- wiki/_Sidebar.md | 8 +- 19 files changed, 271 insertions(+), 494 deletions(-) delete mode 100644 wiki/ALVR-in-distrobox.md delete mode 100644 wiki/Linux-Support-development-progress.md diff --git a/alvr/dashboard/src/dashboard/components/setup_wizard.rs b/alvr/dashboard/src/dashboard/components/setup_wizard.rs index 5700459b35..84babd175c 100644 --- a/alvr/dashboard/src/dashboard/components/setup_wizard.rs +++ b/alvr/dashboard/src/dashboard/components/setup_wizard.rs @@ -75,7 +75,7 @@ impl SetupWizard { ui.horizontal(|ui| { ui.add_space(60.0); - ui.vertical(|ui| { + ui.vertical(|ui: &mut Ui| { ui.add_space(30.0); ui.heading(RichText::new("Welcome to ALVR").size(30.0)); ui.add_space(5.0); diff --git a/wiki/ALVR-client-and-streamer-on-separate-networks.md b/wiki/ALVR-client-and-streamer-on-separate-networks.md index 81ad535817..7615d98b72 100644 --- a/wiki/ALVR-client-and-streamer-on-separate-networks.md +++ b/wiki/ALVR-client-and-streamer-on-separate-networks.md @@ -1,9 +1,16 @@ # Headset and ALVR streamer on separate networks -# ALVR v14 and Above +## ALVR v14 and Above Here are explained two methods to connect PC and headset remotely, port-forwarding and ZeroTier. The primary purpose of this is connecting the headset to a Cloud PC (like ShadowPC). +## Important notes on security + +* ALVR protocol does not have any encryption or authentication (apart from ALVR client IP address shown in ALVR streamer and the requirement to click _Connect_ on ALVR streamer). +* It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR clients should be allowed) and ALVR streamer should not be left running unattended. +* **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR client could take over the PC**). +* As the license states ALVR IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND (see the file `LICENSE` in this GitHub repository for legal text/definition). You are on your own (especially if you run ALVR over the Internet without VPN). + ## Port-forwarding Port-forwarding allows to connect devices that are behind different NATs, i.e. local networks. You need to have administrator access to your router. This method has the best streaming performance. @@ -30,39 +37,39 @@ Pros: * You don't need to update the public IP often on the streamer. * The connection in encrypted. -Cons: +Cons: * The streaming performance is worse. You may experience more glitches and loss of quality in the image and audio. ### Requirements -- [ZeroTier](https://www.zerotier.com/) for your PC -- ZeroTier APK for your Quest (you can find it online) -- SideQuest or some other method to install the ZeroTier APK onto your headset +* [ZeroTier](https://www.zerotier.com/) for your PC +* ZeroTier APK for your Quest (you can find it online) +* SideQuest or some other method to install the ZeroTier APK onto your headset ### Installation -Use the "Install APK" function of SideQuest to install the ZeroTier APK to your Quest, and also download and install ZeroTier on your PC. After you've installed ZeroTier, follow Zerotier's official [Getting Started](https://zerotier.atlassian.net/wiki/spaces/SD/pages/8454145/Getting+Started+with+ZeroTier) guide to setup a network for ALVR. Join the network on both the Quest and the PC. On the Quest, make sure that the network is enabled by switching on the slider on the network in the list in the ZeroTier app (you may be prompted to allow ZeroTier to create a VPN connection). +Use the "Install APK" function of SideQuest to install the ZeroTier APK to your Quest, and also download and install ZeroTier on your PC. After you've installed ZeroTier, follow Zerotier's official [Getting Started](https://zerotier.atlassian.net/wiki/spaces/SD/pages/8454145/Getting+Started+with+ZeroTier) guide to setup a network for ALVR. Join the network on both the Quest and the PC. On the Quest, make sure that the network is enabled by switching on the slider on the network in the list in the ZeroTier app (you may be prompted to allow ZeroTier to create a VPN connection). -After both your PC and your Quest are connected to the same ZeroTier network, we'll need to manually add your quest to the ALVR dashboard. To do so, we'll need to find your Quest's ZeroTier IP. There are two ways to do this. +After both your PC and your Quest are connected to the same ZeroTier network, we'll need to manually add your quest to the ALVR dashboard. To do so, we'll need to find your Quest's ZeroTier IP. There are two ways to do this. -- Go the the ZeroTier network page, find your quest under "Members", and copy the managed IP from there -- Or, in the ZeroTier app on your quest, click on the network you created. The IP is under the "Managed IPs" section at the bottom. +* Go the the ZeroTier network page, find your quest under "Members", and copy the managed IP from there +* Or, in the ZeroTier app on your quest, click on the network you created. The IP is under the "Managed IPs" section at the bottom. -The IP should look something like this `192.168.143.195`. If there's a `/` at the end with a couple numbers following it, remove them along with the slash. +The IP should look something like this `192.168.143.195`. If there's a `/` at the end with a couple numbers following it, remove them along with the slash. -Next, we'll need to add the Quest to the ALVR dashboard. On your headset, launch ALVR. The on the ALVR dashboard on your PC, click the "Add device manually" button, provide a name and hostname (You can get this from the "trust" screen of ALVR on your Quest), then put in the IP address that we got from ZeroTier. +Next, we'll need to add the Quest to the ALVR dashboard. On your headset, launch ALVR. The on the ALVR dashboard on your PC, click the "Add device manually" button, provide a name and hostname (You can get this from the "trust" screen of ALVR on your Quest), then put in the IP address that we got from ZeroTier. At this point, you should be ready to go. Have fun in VR! ### Troubleshooting -- If you can't get your Quest to connect to ALVR, and are stuck on the "Trust" screen, try to ping your Quest's managed IP address (the one we got earlier). If it says "no route to host" or something similar, your Quest can't see your PC. Try running through the steps above to make sure you didn't miss anything. +* If you can't get your Quest to connect to ALVR, and are stuck on the "Trust" screen, try to ping your Quest's managed IP address (the one we got earlier). If it says "no route to host" or something similar, your Quest can't see your PC. Try running through the steps above to make sure you didn't miss anything. ## Tailscale An alternative to ZeroTier with practically the same setup procedure. This could have better latency, depending on your distance to the datacenter. -https://tailscale.com/ + ## n2n @@ -72,46 +79,39 @@ Its pros and cons are similar to ZeroTier, but it's self-hosted and open-source ### Requirements -- Compile [n2n](https://github.com/ntop/n2n) from source - - Or you can grab pre-built binaries from [here](https://github.com/lucktu/n2n) directly, compiled by lucktu. - - Some Linux distribution may have n2n, but be sure you're using the same version. Since the source code is v3, the following steps will also use v3 in the example below. -- [TAP-Windows driver](https://community.openvpn.net/openvpn/wiki/GettingTapWindows) or [OpenVPN](https://openvpn.net/community/) (includes TAP-Windows) if you're using Windows PC -- [hin2n](https://github.com/switch-iot/hin2n) APK -- A server with public IP and allow public ports -- SideQuest or some other method to install the hin2n APK onto your headset +* Compile [n2n](https://github.com/ntop/n2n) from source + * Or you can grab pre-built binaries from [here](https://github.com/lucktu/n2n) directly, compiled by lucktu. + * Some Linux distribution may have n2n, but be sure you're using the same version. Since the source code is v3, the following steps will also use v3 in the example below. +* [TAP-Windows driver](https://community.openvpn.net/openvpn/wiki/GettingTapWindows) or [OpenVPN](https://openvpn.net/community/) (includes TAP-Windows) if you're using Windows PC +* [hin2n](https://github.com/switch-iot/hin2n) APK +* A server with public IP and allow public ports +* SideQuest or some other method to install the hin2n APK onto your headset ### Installation We're going to use n2n v3, and set the port of _supernode_ to `1234` as the example. You can change `1234` to any port, but below `1024` requires root. -- Open port `1234` on your server's firewall (usually `iptables`, if you don't know what to do, ask Google). -- Upload _supernode_ binary to your server, run `./supernode -p 1234`. -- Install TAP-Windows driver or OpenVPN on your PC if you're using Windows. -- Upload _edge_ binary to your PC, run `./edge -c [network-name] -k [secret-password] -a 192.168.100.1 -l [your-server-ip]:1234` to connect to the _supernode_, assign the IP `192.168.100.1` to the PC, and use the password you provided for data encryption. -- Once you see `[OK] edge <<< ================ >>> supernode`, your PC is done, or you need to follow the error logs to see what's wrong. -- Install _hin2n_ on your Quest and open it, click the plus button at the top-right corner to add a new configuration and assign `192.168.100.2` to your Quest: - - N2N version: v3 - - Supernode: `[your-server-ip]:1234` - - Community: `[network-name]` - - Encrypt key: `[secret-password]` - - IP address: `192.168.100.2` - - Subnet mask: `255.255.255.0` -- Click "Current Setting" under the connect button, select the configuration we created just now, then click the connect button. If you're asked to allow hin2n to create a VPN connection, allow it. -- Once you see `[OK] edge <<< ================ >>> supernode`, your Quest is done. -- Open ALVR on your headset, record the hostname it shows. -- Open ALVR dashboard on your PC, click "Add device manually" button, put the hostname you just recorded, and set IP address to `192.168.100.2` which is assigned to Quest just now. -- Once it's done, you're all set. +* Open port `1234` on your server's firewall (usually `iptables`, if you don't know what to do, ask Google). +* Upload _supernode_ binary to your server, run `./supernode -p 1234`. +* Install TAP-Windows driver or OpenVPN on your PC if you're using Windows. +* Upload _edge_ binary to your PC, run `./edge -c [network-name] -k [secret-password] -a 192.168.100.1 -l [your-server-ip]:1234` to connect to the _supernode_, assign the IP `192.168.100.1` to the PC, and use the password you provided for data encryption. +* Once you see `[OK] edge <<< ================ >>> supernode`, your PC is done, or you need to follow the error logs to see what's wrong. +* Install _hin2n_ on your Quest and open it, click the plus button at the top-right corner to add a new configuration and assign `192.168.100.2` to your Quest: + * N2N version: v3 + * Supernode: `[your-server-ip]:1234` + * Community: `[network-name]` + * Encrypt key: `[secret-password]` + * IP address: `192.168.100.2` + * Subnet mask: `255.255.255.0` +* Click "Current Setting" under the connect button, select the configuration we created just now, then click the connect button. If you're asked to allow hin2n to create a VPN connection, allow it. +* Once you see `[OK] edge <<< ================ >>> supernode`, your Quest is done. +* Open ALVR on your headset, record the hostname it shows. +* Open ALVR dashboard on your PC, click "Add device manually" button, put the hostname you just recorded, and set IP address to `192.168.100.2` which is assigned to Quest just now. +* Once it's done, you're all set. ### Troubleshooting -- Make sure you can access to the supernode, your supernode should be run on a server with public IP, and you can ping it on your PC. -- If your Quest cannot connect to ALVR dashboard, ping the IP you assigned to Quest in hin2n. If it fails, try redoing the setup steps. -- If the edge binary or hin2n says the IP has already been assigned and not released by supernode, you can set IP address to another one in the same subnet like `192.168.100.123` to reassign a new IP to the device. -- If you're playing over WAN, you may see more glitches, higher stream latency, or lagger response with TCP. Use adaptive bitrate and UDP may improve your experience. - -**Important notes on security!** - -* ALVR protocol does not have any encryption or authentication (apart from ALVR client IP address shown in ALVR streamer and the requirement to click _Connect_ on ALVR streamer). -* It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR clients should be allowed) and ALVR streamer should not be left running unattended. -* **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR client could take over the PC**). -* As the license states ALVR IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND (see the file `LICENSE` in this GitHub repository for legal text/definition). You are on your own (especially if you run ALVR over the Internet without VPN). +* Make sure you can access to the supernode, your supernode should be run on a server with public IP, and you can ping it on your PC. +* If your Quest cannot connect to ALVR dashboard, ping the IP you assigned to Quest in hin2n. If it fails, try redoing the setup steps. +* If the edge binary or hin2n says the IP has already been assigned and not released by supernode, you can set IP address to another one in the same subnet like `192.168.100.123` to reassign a new IP to the device. +* If you're playing over WAN, you may see more glitches, higher stream latency, or lagger response with TCP. Use adaptive bitrate and UDP may improve your experience. diff --git a/wiki/ALVR-in-distrobox.md b/wiki/ALVR-in-distrobox.md deleted file mode 100644 index 98c0cc791e..0000000000 --- a/wiki/ALVR-in-distrobox.md +++ /dev/null @@ -1,112 +0,0 @@ -## Installing ALVR and using SteamVR on Linux through Distrobox - -## Disclaimer - -1. This is just an attempt to make things easier for Linux users to use ALVR, SteamVR. By no means it's a comprehensive, fully featured and 100% working guide. Please open new issues and pull requests to correct this guide, scripts, etc etc. - -2. Slimevr, OpenVRAS, Open Space Calibrator are all possible to launch and use on Linux, but this guide/script is not adjusted yet (Medium priority TODO). - -3. Firewall configuration is skipped entirely and setup firewall configuration is broken, so it might not work in case you have strict firewall (alvr docs should have info about that) (Low priority TODO). - -4. This script unlikely to work on external disks. - -## Installing alvr distrobox - -For installing you only really need couple of dependencies on host: - -1. `wget` + `curl` (to download podman/distrobox/alvr/etc) -2. `xhost` (on X11 to allow rootless podman to work with graphical applications) -3. `sed` (for removing color in logs) -4. `pipewire` for fully automatic microphone, `pulseaudio` for basic audio support (automatic microphone is unsupported with it) -5. For nvidia - `CUDA` (distrobox passes through it and driver as well into the container and CUDA contains NVENC encoder for streaming) - -After you have installed required dependencies for your installation from above, open terminal in this repository folder and do: - -1. `./setup.sh` - - That's it. **Follow all green and especially red text carefully from the scripts.** - - In case if have errors during installation, please report the full log as-is (remove private info if you happen to have some) as an Issue. - - After full installation, you can use `./start-alvr.sh` to launch alvr automatically. - - Script also downloads related apk file to install to headset into `installation` folder for you. Use Sidequest or ADB to install it. - -## Post-install ALVR & SteamVR Configuration - -After installing ALVR you may want to configure it and steamvr to run at best quality for your given hardware/gpu. Open up ALVR using `./start-alvr.sh` script and do the following (each field with input value needs enter to confirm): - -### Common configuration: - -1. **Resolution:** If you have 6600 XT, 5700 XT, 2060S level GPU you can select Low, and in case you don't mind lower FPS - Medium - -2. **Preferred framerate:** If you know that you will have lower fps than usual (for instance, VRChat), run at lower fps. This is because when reprojection (this is what allows for smooth view despite being at low fps) goes lower than twice the amount of specified framerate - it fails to reproject and will look worse. So for example, you can run at 72hz if you know you're expecting low framerate, and 120hz if you are going to play something like Beat Saber, which is unlikely to run at low fps. - -3. **Encoder preset:** Speed - -4. **Bitrate:** Constant, bitrate: 350-450 mbps for h264 wireless/700 mbit-1 gbit cabled, 100-150 mbps for HEVC (On Pico 4 you can set up to 220 mbps). - -5. **Foveated rendering:** This highly depends on given headset, but generally default settings should be OK for Quest 2. - - For **pico neo 3** and **pico 4** i would recommend setting center region width to 0.8 and height to 0.75, shifts to 0 and edge ratios can be set at 6-7, and for the same **pico neo 3** disable oculus foveation level and dynamic oculus foveation. - -6. **Color correction:** Set sharpening to 1and if you like oversaturated image, bump saturation to 0.6. - -7. For **pico neo 3** and **pico 4** left controller offsets (from top to bottom): - - Position -0.06, -0.03, -0.1; - - Rotation: 0, 3, 17. - -8. **Connection -> Stream Protocol:** TCP. This ensures that there would be no heavy artifacts if packet loss happens (until it's too severe), only slowdowns. - -### AMD-specific configuration: - -1. Preferred codec: HEVC, h264 works too. - -2. Reduce color banding: turn on, might make image smoother. - -### Nvidia-specific configuration (needs feedback): - -1. Preferred codec: h264, HEVC works too - -After that, restart your headset using power button and it will automatically restart steamvr once, applying all changes. - -### SteamVR configuration: - -Inside SteamVR you also may need to change settings to improve experience. Open settings by clicking on triple stripe on SteamVR window and expand Advanced Settings (Hide -> Show) - -1. **Disable SteamVR Home.** It can be laggy, crashes often and generally not working nice on linux, so it is recommend disabling it altogether. - -2. **Render Resolution:** - Custom and keep it at 100%. This is to ensure that SteamVR won't try to supersample resolution given by ALVR - -3. **Video tab: Fade To Grid** on app hang - this will lock your view to last frame when app hangs instead of dropping you into steamvr void, completely optional but you may prefer that. - -4. **Video tab: Disable Advanced Supersample Filtering** - -5. **Video tab: Per-application video settings** - Use Legacy Reprojection Mode for specific game. **This can drastically change experience from being very uncomfortable, rubber-banding, to straight up perfect**. This essentially disables reprojection on SteamVR side and leaves it to the client. Make sure to enable it for each game you will play. - -6. **Developer tab: Set steamvr as openxr runtime** - this ensures that games using openxr (such as Bonelab, or Beat Saber) will use SteamVR. - But do note that Nvidia owners can't launch openxr unity games due to bug in SteamVR Unity plugin - -### Distrobox note: - -* Do note that `sudo` inside container doesn't have privliges to do anything as `root`, but that container has almost exactly the same rights as regular user, so deleting user files from that container **is** possible. - -* You can add your steam library from outside the container after alvr installation as for container, `/home/user` folder is the same as on your host, so you can add it from inside distrobox steam. - -* Do note though, there has been mentioned some issues with mounted devices, symlinks and containers, so in case you have them, please report them to discover if it's the case. - -## Updating ALVR & WlxOverlay - -In case there was an update for ALVR or WlxOverlay in the repository, you can run `./update-vr-apps.sh` with or without prefix. In case you want to manually update ALVR or WlxOverlay versions, you can change `env.sh` file accordingly and run the same script. - -If you want to switch to Nightly version, all you need to do is set `IS_NIGHTLY` in `env.sh` file and re-run `./update-vr-apps.sh` - -## Uninstalling - -To uninstall this, simply run `./uninstall.sh` and it will automatically remove everything related to locally installed distrobox, it's containers, lilipod and everything inside in `installation-lilipod` or prefixed folder. - -## Additional info - -Highly recommend using CoreCtrl (install it using your distribution package management) and setting settings to VR profile for **AMD** gpus, as well as cpu to performance profile (if it's old Ryzen cpu). Without setting those gpu profiles, it's highly likely you will have serious shutters/wobbles/possibly crashes (sway users) at random point while playing ([[PERF] Subpar GPU performance due to wrong power profile mode · Issue #469 · ValveSoftware/SteamVR-for-Linux · GitHub](https://github.com/ValveSoftware/SteamVR-for-Linux/issues/469)). diff --git a/wiki/Building-From-Source.md b/wiki/Building-From-Source.md index 7dd8fe9869..07f8a29858 100644 --- a/wiki/Building-From-Source.md +++ b/wiki/Building-From-Source.md @@ -29,22 +29,19 @@ If you are on Linux, install these additional packages: * `dev-lang/rust >= 1.72` * `media-video/pipewire [jacksdk]` -* **Nix(OS)** - - * Use the `shell.nix` in `packaging/nix`. - * **Debian 12 / Ubuntu 20.04 / Pop!\_OS 20.04** ```bash sudo apt install pulseaudio-utils build-essential pkg-config libclang-dev libssl-dev libasound2-dev libjack-dev libgtk-3-dev libvulkan-dev libunwind-dev gcc yasm nasm curl libx264-dev libx265-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libdrm-dev libva-dev libvulkan-dev vulkan-headers libpipewire-0.3-dev libspa-0.3-dev git ``` - * Note: Libpipewire/libspa must be at least 0.3.49 version - make sure to use upstream pipewire https://github.com/pipewire-debian/pipewire-debian + + * Note: Libpipewire/libspa must be at least 0.3.49 version - make sure to use upstream pipewire * **Fedora** ```bash sudo dnf groupinstall 'Development Tools' | For c++ and build tools - sudo dnf install nasm yasm libdrm-devel vulkan-headers pipewire-jack-audio-connection-kit-devel atk-devel gdk-pixbuf2-devel cairo-devel rust-gdk0.15-devel x264-devel vulkan-devel libunwind-devel clang openssl-devel alsa-lib-devel libva-devel pipewire-devel + sudo dnf install nasm yasm libdrm-devel vulkan-headers pipewire-jack-audio-connection-kit-devel atk-devel gdk-pixbuf2-devel cairo-devel rust-gdk0.15-devel x264-devel vulkan-devel libunwind-devel clang openssl-devel alsa-lib-devel libva-devel pipewire-devel git ``` If you are using Nvidia, see [Fedora cuda installation](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#fedora-cuda-installation) @@ -109,7 +106,7 @@ export PATH=/usr/local/cuda-12.3/bin${PATH:+:${PATH}} If your cuda version is different, change it to the version that is installed. You can check installed versions by doing ```ls /usr/local/ | grep "cuda"``` in your terminal -**Comments** +#### Note about Nvidia's CUDA * Disabling the nvidia-driver doesn't disable Nvidia drivers but prevents nvidia dkms from installing over the akmod driver @@ -128,7 +125,7 @@ Then install gcc11 brew install gcc@11 ``` -**Comments** +#### Notes on installing gcc11 with homebrew * If brew is not found in your path, run the following separately to add brew to your path: @@ -161,13 +158,13 @@ For the client you need install: On Linux, the specific package names for the android tools can differ from distro to distro, see up on the wiki for more information: * Gentoo: - * https://wiki.gentoo.org/wiki/Android + * * Arch: - * https://wiki.archlinux.org/title/Android + * * Debian: - * https://wiki.debian.org/AndroidStudio + * * Ubuntu: - * https://help.ubuntu.com/community/AndroidSDK + * * Pop!\_OS: * N/A @@ -188,7 +185,7 @@ For Debian, it requires to have the `non-free` repository to be enabled: sudo apt install android-sdk-platform-tools-common sdkmanager google-android-ndk-r26b-installer ``` - ## 2. Setting environment variables +## 2. Setting environment variables For Windows, set the environment variables: @@ -202,9 +199,9 @@ For Windows, set the environment variables: For Linux, the correct directories for the environment variables can greatly differ depending on the type of install. See the wiki page of your distro for more information: * Gentoo: - * https://wiki.gentoo.org/wiki/Android + * * Ubuntu: - * https://help.ubuntu.com/community/AndroidSDK#Post-Installation_Configuration + * Distro wikis that weren't listed above does not mention of environment variables, although generally they would be as: @@ -235,6 +232,7 @@ Before building the client, Android has to have us to agree to the licenses othe cd "%ANDROID_SDK_ROOT%\tools\bin" sdkmanager.bat --licenses ``` + * Linux: ```bash diff --git a/wiki/Configuration-Information-and-Recommendations.md b/wiki/Configuration-Information-and-Recommendations.md index bed16f3f19..bd7578e161 100644 --- a/wiki/Configuration-Information-and-Recommendations.md +++ b/wiki/Configuration-Information-and-Recommendations.md @@ -1,40 +1,40 @@ # Information and Recommendations -# PC +## PC - A high-end PC is a requirement; ALVR is not a cheap alternative to a PCVR HMD -- ALVR resolution configuration and SteamVR multi-sampling may be used to influence quality in favor of performance or vice-versa -- Frequent dropped frames can cause a poor experience on ALVR; this can be verified using a tool such as [OVR Advanced Settings](https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings) -- Higher bit-rates will cause higher latency -- Ensure all relevant software is up to date; especially graphics and network drivers -- A good starting point is 100% resolution and 30mbit- 200kb buffer settings. In this config it should be butter smooth with almost no lag or packet loss; packet loss seen at this point is likely a result of network issues +- ALVR resolution configuration and SteamVR multi-sampling may be used to influence quality in favor of performance or vice-versa. +- Frequent dropped frames can cause a poor experience on ALVR; this can be verified using a tool such as [OVR Advanced Settings](https://github.com/OpenVR-Advanced-Settings/OpenVR-AdvancedSettings). +- Higher bit-rates will cause higher latency. +- Ensure all relevant software is up to date - especially graphics and network drivers. +- A good starting point is 100% resolution (`Very low` resolution preset) and 30mbit constant bitrate. In this config it should be very smooth with almost no lag or packet loss; packet loss seen at this point is likely a result of network issues. -# Network +## Network -- A wired connection from the PC to the network is **strongly recommended** -- A modern mid to high-end router and / or access point supporting at least 802.11AC (ideally 802.11AX) with regularly updated firmware is recommended +- A wired connection from the PC to the network is **strongly recommended**. +- A modern mid to high-end router and / or access point supporting at least 802.11ac (ideally 802.11ax) is recommended. ## Wireless -### General WiFi configuration best practices: +### General WiFi configuration best practices -- Any device that can be wired should be; each wireless device slows down the overall wireless network -- Devices should have the fewest obstructions and be as close to the access point or router as possible +- Any device that can be wired should be - each wireless device slows down the overall wireless network +- Devices should have the least amount of obstructions and be as close to the access point or router as possible - Any other wireless networks (ex: a printer's default wireless network) should be disabled; each network slows others down - Any devices that do not need high speeds but support them (ex: a thermostat) should use 2.4Ghz; often middle and higher end access points and routers support methods to "force" clients to use 2.4Ghz, and some can even perform this automatically based on signal strength and connection speed -- Only WiFi revisions which are necessary should be enabled; older standards such as 802.11B, 802.11G, and to a lesser extent, 802.11N, will slow down all clients -- Devices that require high speeds should use: - - 5Ghz only - - The newest WiFi specifications (802.11AX, followed by 802.11AC) - - In most environments, the largest channel width possible (160MHz for 802.11AX, 80MHz in practice for 802.11AC) (**note: some vendors do not set this to the maximum by default**) +- Only WiFi revisions which are necessary should be enabled; older standards such as 802.11b, 802.11g, and to a lesser extent, 802.11n, will slow down all clients +- Devices that require high speeds (such as standalone headset) should use: + - 5GHz only + - The newest WiFi specifications (802.11ax, followed by 802.11ac) + - In most environments, the largest channel width possible (160MHz for 802.11ax, 80MHz in practice for 802.11ac) (**note: some vendors do not set this to the maximum by default**) - The lowest utilization, followed by the lowest channel number (sub-frequency) possible -- **Manually selecting channels should only be done in places with extreme noise, or on older, lower quality, or ISP provided access points or routers** ; modern mid to high-end routers and access points should optimize their channels fairly well, and as a result of other routers and clients "channel hopping", static settings are often less optimal -- If a specific WiFi channel range is absolutely necessary, use a WiFi scanning tool on a phone or PC to determine the least used channels; mid to high-end access points and routers may provide an interface for this as well, however, this sometimes causes a disconnect when scanning -- **Manually selecting wifi signal strength should only be done in places with extreme noise**; modern routers and access points do this well, and it is a complex task -- If a specific transmit power is necessary, keep in mind that stronger is not always better; as transmit power increases, distortion may increase (leading to *lower* speeds), battery life of clients may increase (due to the higher power requested by the access point or router), and issues with sticky clients (devices which stay connected to wifi even with bad signal) may appear +- **Manually selecting channels should only be done in places with extreme noise, or on older, lower quality, or ISP provided access points or routers** - modern mid to high-end routers and access points should optimize their channels fairly well, and as a result of other routers and clients "channel hopping", static settings are often less optimal +- If a specific WiFi channel range is absolutely necessary, use a WiFi scanning tool on a phone or PC to determine the least used channels - mid to high-end access points and routers may provide an interface for this as well, however, this sometimes causes a disconnect when scanning +- **Manually selecting wifi signal strength should only be done in places with extreme noise** - modern routers and access points do this well, and it is a complex task +- If a specific transmit power is necessary, keep in mind that stronger is not always better - as transmit power increases, distortion may increase (leading to *lower* speeds), battery life of clients may increase (due to the higher power requested by the access point or router), and issues with sticky clients (devices which stay connected to wifi even with bad signal) may appear - If you have a significant number of devices, some routers and access points support features such as airtime fairness, which help to limit the amount of airtime slower clients take, improving the performance of higher speed clients -### Things to keep in mind when configuring a wireless network and devices: +### Things to keep in mind when configuring a wireless network and devices - All devices on the same frequency impact each other (**including other WiFi networks on the same channel**) because only one device can transmit or receive data at a time, meaning that: - If one device utilizes WiFi heavily it will impact the latency and throughput of all other clients @@ -47,22 +47,22 @@ ## Routing / Switching / Firewalling / General Info -- Ideally client and streamer should live on the same logical (layer 2) network and subnet; this allows for no routing overhead, and the correct function of client discovery via mDNS -- Twisted pair (normal copper ethernet cables) should never be run alongside power cables; this can cause signal noise and result in frame loss and lowered auto-negotiation speeds -- High quality CAT5E or higher (ideally CAT6A or CAT7) cabling should be used for modern networks -- In some cases firewall, anti-virus, malware, or EDR (enhanced detection and response) software may interfere with network traffic; Windows Defender and Sophos Endpoint Protection are reported to work without issue +- Ideally client and streamer should exist on the same logical (layer 2) network and subnet - this allows for no routing overhead, and the correct function of client discovery via [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) +- Twisted pair (normal copper ethernet cables) should never be run alongside power cables - this can cause signal noise and result in frame loss and lowered auto-negotiation speeds +- High quality CAT5E or higher (ideally CAT6A or CAT7) gigabit+ cabling should be used for modern networks +- In some cases firewall, anti-virus, malware, or EDR (enhanced detection and response) software may interfere with network traffic - Windows Defender and Sophos Endpoint Protection are reported to work without issue - Pause frames should be disabled where possible, as these introduce additional latency and buffering *** Someone did a few blog-posts on some of the points: -https://imaginevr.home.blog/author/imaginevrresearch/ + Some points came from [FingrMastr](https://github.com/FingrMastr) -# Linux +## Linux -## Encoder requirements +### Encoder requirements ALVR uses FFmpeg for all encoders, so you will need to make sure the encoder of your choice works with FFmpeg. Always consult Log tab in dashboard, it will tell you the reason why an encoder failed to initialize. @@ -97,7 +97,7 @@ vainfo: Supported profile and entrypoints *VAProfileH264High, VAProfileHEVCMain, VAProfileHEVCMain10* encoders (VAEntrypointEncSlice) required. If you don't see those in your output, your driver install is incorrect or your distribution decided to build *mesa* without non-free codecs. -**Test ffmpeg commands** +#### Test ffmpeg commands (VAAPI) ```sh # H264 @@ -107,11 +107,11 @@ ffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=n ffmpeg -vaapi_device /dev/dri/renderD128 -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v hevc_vaapi vaapi-hevc.mp4 ``` -### NVENC (NVidia) +### NVENC (Nvidia) Requires *libcuda*. -**Test ffmpeg commands** +#### Test ffmpeg commands (Nvidia) ```sh # H264 @@ -121,18 +121,7 @@ ffmpeg -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v h264_nvenc nven ffmpeg -f lavfi -i testsrc -t 30 -vf 'format=nv12,hwupload' -c:v hevc_nvenc nvenc-hevc.mp4 ``` -### Software (all GPUs) +### Software (any GPUs) Software encoder is mainly used as a fallback and as such should work on all GPUs without any requirements. Only H264 encoding is currently supported. - -## Legacy Reprojection - -SteamVR by default will still use async reprojection for all games, which can cause issues such as: - -- Ghosting -- Jumpy framerate -- Jittery movement -- General unsmoothness - And all other sorts of issues. Turning on Legacy Reprojection in the per game video settings inside SteamVR will effectively disable - any reprojection making the experience far better. diff --git a/wiki/Controller-latency.md b/wiki/Controller-latency.md index c65798ab20..7cdca07909 100644 --- a/wiki/Controller-latency.md +++ b/wiki/Controller-latency.md @@ -1,3 +1,5 @@ +# Controller latency + Controller tracking will always be difficult. There are so many factors that influence the latency and the motion prediction. Its not something like "100ms" constantly, but depends on your movements and even the movement of the headset. There are many parameters that influence the movement that can be changed: diff --git a/wiki/Flatpak.md b/wiki/Flatpak.md index 7085328169..4ed84b65c7 100644 --- a/wiki/Flatpak.md +++ b/wiki/Flatpak.md @@ -1,10 +1,10 @@ -## Installing ALVR and using SteamVR on Linux through Flatpak +# Installing ALVR and using SteamVR on Linux through Flatpak ## Disclaimer 1. This is not a fully-featured version of ALVR! It lacks Nvidia support and has bugs related to Flatpak sandboxing -2. Nvidia GPUs are currently not supported +2. Nvidia GPUs are currently not supported (but might be supported with [this PR](https://github.com/alvr-org/ALVR/pull/2207)) 3. Native Linux SteamVR utility applications such as OpenVRAS are not supported nor tested, use at your own risk @@ -16,11 +16,11 @@ 7. The ALVR Dashboard is not available in the Applications menu. To run the dashboard, run the following command to run `alvr_dashboard` in the Steam Flatpak environment: -``` +```sh flatpak run --command=alvr_dashboard com.valvesoftware.Steam ``` -8. This only works with the Steam Flatpak. For non-Flatpak Steam, use the AppImage instead +8. This only works with the Steam Flatpak. For non-Flatpak Steam, use the launcher or tar.gz ## Dependencies @@ -35,7 +35,7 @@ Once Flatpak is installed, the flatpak dependencies must also be installed. They These can be installed like so: -``` +```sh flatpak install flathub org.freedesktop.Sdk//23.08 \ org.freedesktop.Sdk.Extension.llvm16//23.08 \ org.freedesktop.Sdk.Extension.rust-stable//23.08 \ @@ -44,7 +44,7 @@ flatpak install flathub org.freedesktop.Sdk//23.08 \ AMD users may need to install the appropriate Mesa codec extensions as well: -``` +```sh flatpak install flathub org.freedesktop.Platform.GL.default//23.08-extra \ org.freedesktop.Platform.GL32.default//23.08-extra ``` @@ -53,7 +53,7 @@ flatpak install flathub org.freedesktop.Platform.GL.default//23.08-extra \ Install SteamVR via the Steam Flatpak. After installing SteamVR, run the following command: -``` +```sh sudo setcap CAP_SYS_NICE+eip ~/.var/app/com.valvesoftware.Steam/data/Steam/steamapps/common/SteamVR/bin/linux64/vrcompositor-launcher ``` @@ -63,7 +63,7 @@ This command is normally run by SteamVR, but due to the lack of sudo access with Download `com.valvesoftware.Steam.Utility.alvr.flatpak` file from one of the latest [nightly](https://github.com/alvr-org/ALVR-nightly/releases) that contains flatpak bundle and install like so: -``` +```sh flatpak --user install --bundle com.valvesoftware.Steam.Utility.alvr.flatpak ``` @@ -75,26 +75,26 @@ Alternatively, if the file is not available or a newer version is needed, the fl First, the dependencies from above must be fulfilled. Then, install `flatpak-builder` like so: -``` +```sh flatpak install flathub org.flatpak.Builder ``` Once the dependencies are fulfilled, clone and enter the repository. -``` +```sh git clone https://github.com/alvr-org/ALVR.git cd ALVR ``` Once inside the repository, simply run the following command to build and install the Flatpak. -``` +```sh flatpak run org.flatpak.Builder --user --install --force-clean .flatpak-build-dir alvr/xtask/flatpak/com.valvesoftware.Steam.Utility.alvr.json ``` If ALVR is not cloned under the home directory, permission to access the directory may need to be given to the build command. An example of this is given below. -``` +```sh flatpak run --filesystem="$(pwd)" org.flatpak.Builder --user --install --force-clean .flatpak-build-dir alvr/xtask/flatpak/com.valvesoftware.Steam.Utility.alvr.json ``` @@ -104,28 +104,12 @@ flatpak run --filesystem="$(pwd)" org.flatpak.Builder --user --install --force-c To run the ALVR Dashboard, run the following command: -``` +```sh flatpak run --command=alvr_dashboard com.valvesoftware.Steam ``` A desktop file named `com.valvesoftware.Steam.Utility.alvr.desktop` is supplied within the `alvr/xtask/flatpak` directory. Move this to where other desktop files are located on your system in order to run the dashboard without the terminal. -### Automatic Audio & Microphone setup - -Currently the game audio and microphone to and from the headset isn't routed automatically. The setup of this script will therefore run every time the headset connects or disconnects to the ALVR dashboard. This is based on [the steps](Installation-guide.md#automatic-audio--microphone-setup) in the installation guide, modified for the Flatpak. - -1. In the ALVR Dashboard under All Settings (Advanced) > Audio, enable Game Audio and Microphone. - -2. In the same place under Microphone, click Expand and set Devices to custom. Enter `default` for the name for both Sink and Source. - -3. Download the [audio-flatpak-setup.sh](../alvr/xtask/flatpak/audio-flatpak-setup.sh) script and place it into the Flatpak app data directory located at `~/.var/app/com.valvesoftware.Steam/`. Make sure it has execute permissions (e.g. `chmod +x audio-flatpak-setup.sh`). - -5. In the ALVR Dashboard, under All Settings (Advanced) > Connection, set the On connect script and On disconnect script to the absolute path of the script (relative to the Flatpak environment), e.g. `/home/$USER/.var/app/com.valvesoftware.Steam/audio-flatpak-setup.sh`. - -6. In a terminal, run `flatpak override --user --filesystem=xdg-run/pipewire-0 com.valvesoftware.Steam` to allow the script to set and map your headset's microphone - -7. Restart both Steam and the ALVR Dashboard - ### Other Applications The support for other applications that are not launched via Steam is non-existent due to the Flatpak sandbox. diff --git a/wiki/Hardware-Video-Encoding.md b/wiki/Hardware-Video-Encoding.md index 9ac6aca1ea..a6db34c828 100644 --- a/wiki/Hardware-Video-Encoding.md +++ b/wiki/Hardware-Video-Encoding.md @@ -2,89 +2,91 @@ FFmpeg hardware video offloading test commands to validate hardware encoding offloading is working. -Learn more at: https://trac.ffmpeg.org/wiki/HWAccelIntro +Learn more at: -### Codecs +## Codecs -* **Advanced Video Coding (AVC/h264)** - https://en.wikipedia.org/wiki/Advanced_Video_Coding +* **Advanced Video Coding (AVC/h264)** - - Advanced Video Coding (AVC), also referred to as H.264 or MPEG-4 Part 10, is a video compression standard based on block-oriented, motion-compensated coding. It is by far the most commonly used format for the recording, compression, and distribution of video content. It supports a maximum resolution of 8K UHD. Hardware encoding support is widely available. + Advanced Video Coding (AVC), also referred to as H.264 or MPEG-4 Part 10, is a video compression standard based on block-oriented, motion-compensated coding. It is by far the most commonly used format for the recording, compression, and distribution of video content. It supports a maximum resolution of 8K UHD. Hardware encoding support is widely available. -* **High Efficiency Video Coding (HEVC/h265)** - https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding +* **High Efficiency Video Coding (HEVC/h265)** - - High Efficiency Video Coding (HEVC), also known as H.265 and MPEG-H Part 2, is a video compression standard designed as part of the MPEG-H project as a successor to the widely used Advanced Video Coding (AVC, H.264, or MPEG-4 Part 10). In comparison to AVC, HEVC offers from 25% to 50% better data compression at the same level of video quality, or substantially improved video quality at the same bit rate. It supports resolutions up to 8192×4320, including 8K UHD, and unlike the primarily 8-bit AVC, HEVC's higher fidelity Main 10 profile has been incorporated into nearly all supporting hardware. Hardware encoding support is widely available. + High Efficiency Video Coding (HEVC), also known as H.265 and MPEG-H Part 2, is a video compression standard designed as part of the MPEG-H project as a successor to the widely used Advanced Video Coding (AVC, H.264, or MPEG-4 Part 10). In comparison to AVC, HEVC offers from 25% to 50% better data compression at the same level of video quality, or substantially improved video quality at the same bit rate. It supports resolutions up to 8192×4320, including 8K UHD, and unlike the primarily 8-bit AVC, HEVC's higher fidelity Main 10 profile has been incorporated into nearly all supporting hardware. Hardware encoding support is widely available. -* **AOMedia Video 1 (AV1)** - https://en.wikipedia.org/wiki/AV1 +* **AOMedia Video 1 (AV1)** - - AOMedia Video 1 (AV1) is an open, royalty-free video coding format initially designed for video transmissions over the Internet. It was developed as a successor to VP9 by the Alliance for Open Media (AOMedia). The AV1 bitstream specification includes a reference video codec. Hardware encoding support is limited to latest generation hardware. + AOMedia Video 1 (AV1) is an open, royalty-free video coding format initially designed for video transmissions over the Internet. It was developed as a successor to VP9 by the Alliance for Open Media (AOMedia). The AV1 bitstream specification includes a reference video codec. Hardware encoding support is limited to latest generation hardware. ### Graphics Encoding APIs -* **Video Acceleration API** - https://en.wikipedia.org/wiki/Video_Acceleration_API +* **Video Acceleration API** - - Video Acceleration API (VA-API) is an open source application programming interface that allows applications such as VLC media player or GStreamer to use hardware video acceleration capabilities, usually provided by the graphics processing unit (GPU). It is implemented by the free and open-source library libva, combined with a hardware-specific driver, usually provided together with the GPU driver. - - Check your current VA-API status with `vainfo`. + Video Acceleration API (VA-API) is an open source application programming interface that allows applications such as VLC media player or GStreamer to use hardware video acceleration capabilities, usually provided by the graphics processing unit (GPU). It is implemented by the free and open-source library libva, combined with a hardware-specific driver, usually provided together with the GPU driver. -* **Vulkan Video** - https://en.wikipedia.org/wiki/Vulkan + Check your current VA-API status with `vainfo`. - Vulkan is a low-level low-overhead, cross-platform API and open standard for 3D graphics and computing. It was intended to address the shortcomings of OpenGL, and allow developers more control over the GPU. It is designed to support a wide variety of GPUs, CPUs and operating systems, it is also designed to work with modern multi-core CPUs. Support is upcoming. See: https://www.khronos.org/blog/khronos-releases-vulkan-video-av1-decode-extension-vulkan-sdk-now-supports-h.264-h.265-encode +* **Vulkan Video** - -* **NVENC** - https://en.wikipedia.org/wiki/Nvidia_NVENC + Vulkan is a low-level low-overhead, cross-platform API and open standard for 3D graphics and computing. It was intended to address the shortcomings of OpenGL, and allow developers more control over the GPU. It is designed to support a wide variety of GPUs, CPUs and operating systems, it is also designed to work with modern multi-core CPUs. Support is upcoming. See: - Nvidia NVENC is a feature in Nvidia graphics cards that performs video encoding, offloading this compute-intensive task from the CPU to a dedicated part of the GPU. +* **NVENC** - -* **AMD Advanced Media Framework**- https://gpuopen.com/advanced-media-framework/ + Nvidia NVENC is a feature in Nvidia graphics cards that performs video encoding, offloading this compute-intensive task from the CPU to a dedicated part of the GPU. - AMD AMF is a SDK for optimal access to AMD GPUs for multimedia processing. +* **AMD Advanced Media Framework**- + + AMD AMF is a SDK for optimal access to AMD GPUs for multimedia processing. ### Test Source Input Generation + Please note that using the test source for input generation induces CPU load. When monitoring for proper GPU offloading, there will still be expected CPU load from FFmpeg. -``` +```sh ffmpeg -hide_banner -f lavfi -i testsrc2=duration=30:size=1280x720:rate=90 ``` -* **lavfi** - https://ffmpeg.org/ffmpeg-devices.html#toc-lavfi +* **lavfi** - - Libavfilter input virtual device. This input device reads data from the open output pads of a libavfilter filtergraph. For each filtergraph open output, the input device will create a corresponding stream which is mapped to the generated output. The filtergraph is specified through the option graph. + Libavfilter input virtual device. This input device reads data from the open output pads of a libavfilter filtergraph. For each filtergraph open output, the input device will create a corresponding stream which is mapped to the generated output. The filtergraph is specified through the option graph. -* **testsrc2** - https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-colorchart_002c-colorspectrum_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc +* **testsrc2** - - The `testsrc2` source generates a test video pattern, showing a color pattern, a scrolling gradient and a timestamp. This is mainly intended for testing purposes. The `testsrc2` source is similar to `testsrc`, but supports more pixel formats instead of just `rgb24`. This allows using it as an input for other tests without requiring a format conversion. - - 1) duration - how long of a clip in seconds - 2) size - dimensions of the video - 3) rate - frame rate per second + The `testsrc2` source generates a test video pattern, showing a color pattern, a scrolling gradient and a timestamp. This is mainly intended for testing purposes. The `testsrc2` source is similar to `testsrc`, but supports more pixel formats instead of just `rgb24`. This allows using it as an input for other tests without requiring a format conversion. + + 1) duration - how long of a clip in seconds + 2) size - dimensions of the video + 3) rate - frame rate per second ### Render Playback + Use your favorite video player to verify the video was rendered correctly. -* MPV - https://en.wikipedia.org/wiki/Mpv_(media_player) +* MPV - - mpv is free and open-source media player software based on MPlayer, mplayer2 and FFmpeg. It runs on several operating systems, including Unix-like operating systems (Linux, BSD-based, macOS) and Microsoft Windows, along with having an Android port called mpv-android. It is cross-platform, running on ARM, PowerPC, x86/IA-32, x86-64, and MIPS architecture. + mpv is free and open-source media player software based on MPlayer, mplayer2 and FFmpeg. It runs on several operating systems, including Unix-like operating systems (Linux, BSD-based, macOS) and Microsoft Windows, along with having an Android port called mpv-android. It is cross-platform, running on ARM, PowerPC, x86/IA-32, x86-64, and MIPS architecture. -* VLC - https://en.wikipedia.org/wiki/VLC_media_player +* VLC - - VLC media player (previously the VideoLAN Client and commonly known as simply VLC) is a free and open-source, portable, cross-platform media player software and streaming media server developed by the VideoLAN project. VLC is available for desktop operating systems and mobile platforms, such as Android, iOS and iPadOS. VLC is also available on digital distribution platforms such as Apple's App Store, Google Play, and Microsoft Store. + VLC media player (previously the VideoLAN Client and commonly known as simply VLC) is a free and open-source, portable, cross-platform media player software and streaming media server developed by the VideoLAN project. VLC is available for desktop operating systems and mobile platforms, such as Android, iOS and iPadOS. VLC is also available on digital distribution platforms such as Apple's App Store, Google Play, and Microsoft Store. ### Nvidia GPU -Test the Nvidia hardware encoding pipeline. Only NVENC is supported as the current Nvidia VA-API driver (https://github.com/elFarto/nvidia-vaapi-driver) only supports NVDEC. Check your hardware support at https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new for NVENC support. -Monitoring utilities: +Test the Nvidia hardware encoding pipeline. Only NVENC is supported as the current Nvidia VA-API driver () only supports NVDEC. Check your hardware support at for NVENC support. -* **nvtop** - https://github.com/Syllo/nvtop +Monitoring utilities: - NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. +* **nvtop** - -* **nvidia-smi pmon** - https://developer.nvidia.com/nvidia-system-management-interface + NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. - The NVIDIA System Management Interface (nvidia-smi) is a command line utility, based on top of the NVIDIA Management Library (NVML), intended to aid in the management and monitoring of NVIDIA GPU devices. The `pmon` command lists the statistics for all the compute and graphics processes running on each device. +* **nvidia-smi pmon** - + The NVIDIA System Management Interface (nvidia-smi) is a command line utility, based on top of the NVIDIA Management Library (NVML), intended to aid in the management and monitoring of NVIDIA GPU devices. The `pmon` command lists the statistics for all the compute and graphics processes running on each device. Nvenc AVC (h264) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -c:v h264_nvenc -qp 18 \ @@ -93,7 +95,7 @@ nvidia-h264_nvec-90fps-300s.mp4 Nvenc HEVC (h265) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -c:v hevc_nvenc -qp 18 \ @@ -102,7 +104,7 @@ nvidia-hevc_nvec-90fps-300s.mp4 Nvenc AV1 hardware encoding (Ada Lovelace or newer hardware): -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -c:v av1_nvenc -qp 18 \ @@ -110,17 +112,18 @@ nvidia-av1_nvec-90fps-300s.mp4 ``` ### Intel GPU -Test the Intel hardware encoding pipeline. Only VA-API is supported with the intel-media-driver (https://github.com/intel/media-driver) on GEN based graphics hardware. Check your hardware support at https://www.intel.com/content/www/us/en/docs/onevpl/developer-reference-media-intel-hardware/1-1/overview.html for encoding codec support. + +Test the Intel hardware encoding pipeline. Only VA-API is supported with the intel-media-driver () on GEN based graphics hardware. Check your hardware support at for encoding codec support. Monitoring utilities: -* **nvtop** - https://github.com/Syllo/nvtop +* **nvtop** - - NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. + NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. VA-API AVC (h264) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ @@ -130,7 +133,7 @@ intel-h264_vaapi-90fps-300s.mp4 VA-API HEVC (h265) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ @@ -140,7 +143,7 @@ intel-hevc_vaapi-90fps-300s.mp4 VA-API AV1 hardware encoding (Arc A-Series only): -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ @@ -149,17 +152,18 @@ intel-av1_vaapi-90fps-300s.mp4 ``` ### AMD GPU -Test the AMD hardware encoding pipeline. Only VA-API is supported with the mesa-va-drivers (https://mesa3d.org/) on AMD based graphics hardware. Check your hardware support at https://en.wikipedia.org/wiki/Unified_Video_Decoder for encoding codec support. Video Core Next (VCN) hardware is required for hardware encoding. + +Test the AMD hardware encoding pipeline. Only VA-API is supported with the mesa-va-drivers () on AMD based graphics hardware. Check your hardware support at for encoding codec support. Video Core Next (VCN) hardware is required for hardware encoding. Monitoring utilities: -* **nvtop** - https://github.com/Syllo/nvtop +* **nvtop** - - NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. + NVTOP stands for Neat Videocard TOP, a (h)top like task monitor for AMD, Intel and NVIDIA GPUs. It can handle multiple GPUs and print information about them in a htop-familiar way. VA-API AVC (h264) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ @@ -169,7 +173,7 @@ amd-h264_vaapi-90fps-300s.mp4 VA-API HEVC (h265) hardware encoding: -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ @@ -179,11 +183,10 @@ amd-hevc_vaapi-90fps-300s.mp4 VA-API AV1 hardware encoding (VCN 4.0+, Navi 3x only): -``` +```sh ffmpeg -hide_banner \ -f lavfi -i testsrc2=duration=300:size=1280x720:rate=90 \ -vaapi_device /dev/dri/renderD128 -vf 'format=nv12,hwupload' \ -c:v av1_vaapi -qp 18 \ amd-av1_vaapi-90fps-300s.mp4 ``` - diff --git a/wiki/Home.md b/wiki/Home.md index d09dedb107..9f03e12f58 100644 --- a/wiki/Home.md +++ b/wiki/Home.md @@ -1,3 +1,3 @@ -ALVR is a piece of software to stream SteamVR games to your standalone VR headset. +ALVR is a vr streaming software that allows you to stream SteamVR games to your standalone VR headset. Use the sidebar to navigate the wiki. diff --git a/wiki/Installation-guide.md b/wiki/Installation-guide.md index 352d56b516..b51e9db846 100644 --- a/wiki/Installation-guide.md +++ b/wiki/Installation-guide.md @@ -1,114 +1,84 @@ # Installation guide -## Basic installation +## Launcher (BETA) -PC side: +Launcher will allow you to manage old, current and new installations of ALVR streamer and allow to automatically install and upgrade to specific ALVR client version on headset -* Install SteamVR, **launch it once** then close it. This is to make sure it sets the environment correctly for ALVR. -* Go to the latest release [download page](https://github.com/alvr-org/ALVR/releases/latest). In the "Assets" section at the bottom download the ALVR Installer. -* Run the installer. If prompted, allow the execution in the SmartScreen popup. You need to give administrator permissions to install ALVR. For best compatibility do not change the installation folder. -* Once the installation finished, launch ALVR. You are greeted with a setup wizard. Follow the setup to set the firewall rules and presets. +### Installation -**If you have problems launching ALVR, follow the guide below to use the portable version** - -Headset side: - -* Install SideQuest on your PC and enable developer mode on the headset. You can follow [this guide](https://sidequestvr.com/setup-howto). -* Connect your headset to Sidequest. If you have an Oculus Quest 1/2 download the ALVR app [here](https://sidequestvr.com/app/9), if you have an Oculus Go download it [here](https://sidequestvr.com/app/2658) +* Download `alvr_launcher_windows.zip` on windows or `alvr_launcher_linux.tar.gz` from the release [download page](https://github.com/alvr-org/ALVR/releases/latest) and extract into a path that contains only ASCII characters (english only) and has edit permissions without administrator or root rights. +* Run `ALVR Launcher.exe` (on Windows) or `alvr_launcher_linux/ALVR Launcher` (on Linux) +* Press `Add version` button +* For default installation keep channel and version as is and press `Install` +* Wait until it finishes downloading, installing (depends on your connection) +* To install ALVR client on headset, use button `Install APK` +* In the list, to open streamer app (PC) press `Launch`. You will be greeted with a setup wizard. Follow the setup to set the firewall rules and other settings. ### Usage -* Launch ALVR on your headset. While the headset screen is on, click `Trust` next to the client entry (on the PC) to start streaming. +* Before launching SteamVR through ALVR, please install it and run at least once without ALVR and close it. +* Launch ALVR client on your headset. While the headset screen is on, click `Trust` next to the client entry (in the ALVR streamer app on PC, in the `Devices` tab) to start streaming. * You can change settings on the PC in the `Settings` tab. Most of the settings require to restart SteamVR to be applied. Use the apposite button on the bottom right corner. For any problem visit the [Troubleshooting page](https://github.com/alvr-org/ALVR/wiki/Troubleshooting). -## Advanced installation - -### Portable version - -There is also a portable version for the PC that requires more manual steps to make it work. +### Windows microphone streaming -* Install SteamVR and launch it once. -* Download `alvr_streamer_windows.zip` from the latest release [download page](https://github.com/alvr-org/ALVR/releases/latest). -* Unzip into a path that contains only ASCII characters and has edit permissions without administrator rights. +To use the microphone you need to install the [VB-CABLE driver](https://vb-audio.com/Cable/). +Set "CABLE Output" as the default microphone. +Then you can enable the microphone in the ALVR setting, leave "Virtual microphone input" to Default. -### Nightly +## Advanced installation -If you want to get new features early or you want to help with testing you can install a nightly version. +### Installing client using Sidequest -Download the latest nightly streamer [here](https://github.com/alvr-org/ALVR-nightly/releases/latest). Download the latest nightly client from Sidequest ([download](https://sidequestvr.com/app/2281)). +* Install SideQuest on your PC and enable developer mode on the headset. You can follow [this guide](https://sidequestvr.com/setup-howto). +* Connect your headset to Sidequest. If you have Quest, Pico, and other compatible device download the ALVR app [here](https://sidequestvr.com/app/9) -Since nightly releases can be unstable, always use matching versions for PC and headset. They are updated once a day. +### Manually installing ALVR streamer -### Windows microphone streaming +There is also a portable version for the PC that requires more manual steps to make it work. -To use the microphone you need to install the [VB-CABLE driver](https://vb-audio.com/Cable/). Set "CABLE Output" as the default microphone. Then you can enable the microphone in the ALVR setting, leave "Virtual microphone input" to Default. +#### Windows -### Use ALVR together with third-party drivers +* Download `alvr_streamer_windows.zip` from the latest release [download page](https://github.com/alvr-org/ALVR/releases/latest). +* Unzip into a path that contains only ASCII characters and has edit permissions without administrator rights. +* Run -By default ALVR disables other SteamVR drivers before starting. Among these drivers there is [Driver4VR](https://www.driver4vr.com/) for full body tracking. ALVR disables these drivers to maximize compatibility with every PC setup. You can disable this behavior by manually registering the ALVR driver. Go to the `installation` tab and click on `Register ALVR driver`. The next time you launch ALVR you will be able to use the other drivers concurrently. +#### Linux -### Launch ALVR together with SteamVR +* Download `alvr_streamer_linux.tar.gz` from the release [download page](https://github.com/alvr-org/ALVR/releases/latest), extract it. +* Run `bin/alvr_dashboard` -You can skip the ALVR Dashboard and open ALVR automatically together with SteamVR. Open ALVR, go to the `Installation` tab and click on `Register ALVR driver`. +#### Nightly -### Connect headset and PC via a USB Cable +If you want to get new features early or you want to help with testing you can install a nightly version. -Check out the guide [here](https://github.com/alvr-org/ALVR/wiki/Using-ALVR-through-a-USB-connection). +Download the latest nightly streamer [here](https://github.com/alvr-org/ALVR-nightly/releases/latest). -## Linux +Since nightly releases can be unstable, always use matching versions for PC and headset. They are updated once a day. ### Arch Linux (AUR) * Install `rustup` and a rust toolchain, if you don't have it: . -* Install [alvr](https://aur.archlinux.org/packages/alvr)AUR (recommended), or [alvr-git](https://aur.archlinux.org/packages/alvr-git)AUR +* Install [alvr](https://aur.archlinux.org/packages/alvr)AUR (stable, amdgpu), or [alvr-nvidia](https://aur.archlinux.org/packages/alvr-nvidia)AUR (stable, nvidia), or [alvr-git](https://aur.archlinux.org/packages/alvr-git)AUR(nightly, unstable) * Install SteamVR, **launch it once** then close it. * Run `alvr_dashboard` or ALVR from your DE's application launcher. -### Semi-Automatic Distrobox installation and guidance - -Notes: - -* This should be used in case you can't install or have issues with ALVR on your system. Do note that on Nvidia it uses host drivers and cuda nvenc, so if you're on older distribution like Debian and having older driver and cuda version, it will not work properly either way. - -* Guide also contains fixes, tweaks, additional software like desktop overlay to help you run with steamvr better and workaround it's issues. - -Installation: - -1. Download zip from https://github.com/alvr-org/ALVR-Distrobox-Linux-Guide, unpack it somewhere in your home directory (steam doesn't like long paths) or `git clone https://github.com/alvr-org/ALVR-Distrobox-Linux-Guide.git`. - - If you want to use nightly (potentially unstable, but fresh) builds, set `IS_NIGHTLY=0` variable in `env.sh` to `IS_NIGHTLY=1`. - -2. `cd ALVR-Distrobox-Linux-Guide` - -3. Carefully follow the [guide](ALVR-in-distrobox.md). - -4. Any issues related to this installer/various tweaks/bugs should be reported as issue [here](https://github.com/alvr-org/ALVR-Distrobox-Linux-Guide/issues) - -### Other - -#### AppImage - -You can get appimage for latest stable version from [here](https://github.com/alvr-org/ALVR/releases/latest). - -#### Flatpak +### Flatpak For Flatpak users, refer to the instructions [here](https://github.com/alvr-org/ALVR/wiki/Flatpak) -#### Portable tar.gz +## Advanced usage -* Install FFmpeg with VAAPI/NVENC + DRM + Vulkan + x264/x265 support. You can use this [ppa:savoury1/ffmpeg5](https://launchpad.net/~savoury1/+archive/ubuntu/ffmpeg5) under Ubuntu. -* Install SteamVR, **launch it once** then close it. -* Download `alvr_streamer_linux.tar.gz` from the release [download page](https://github.com/alvr-org/ALVR/releases/latest). -* Run `bin/alvr_dashboard` +### Use ALVR together with third-party drivers -### Automatic Audio & Microphone Setup +By default ALVR disables other SteamVR drivers before starting. Among these drivers there is [Driver4VR](https://www.driver4vr.com/) for full body tracking. ALVR disables these drivers to maximize compatibility with every PC setup. You can disable this behavior by manually registering the ALVR driver. Go to the `installation` tab and click on `Register ALVR driver`. The next time you launch ALVR you will be able to use the other drivers concurrently. -* Must be on v20.5.0+ +### Launch ALVR together with SteamVR -* Pipewire required +You can skip the ALVR Dashboard and open ALVR automatically together with SteamVR. Open ALVR, go to the `Installation` tab and click on `Register ALVR driver`. -* Open installation -> Run setup wizard, skip to part with automatic audio setup +### Connect headset to PC via a USB Cable -* Press the button to automatically download and set it +Check out the guide [here](https://github.com/alvr-org/ALVR/wiki/Using-ALVR-through-a-USB-connection). diff --git a/wiki/Linux-Support-development-progress.md b/wiki/Linux-Support-development-progress.md deleted file mode 100644 index 898be240e6..0000000000 --- a/wiki/Linux-Support-development-progress.md +++ /dev/null @@ -1,92 +0,0 @@ -**Warning:** This page is very outdated, see [Building From Source](https://github.com/alvr-org/ALVR/wiki/Building-From-Source) instead. - -## 2022-01-04 - -An experimental NVENC fork has successfully been created by [Toxblh](https://github.com/Toxblh), helping fix one of the larger bottlenecks on NVIDIA GPUs. [Pull Request here](https://github.com/alvr-org/ALVR/pull/906) - -## 2021-05-18 - -No special build steps are required for users who can acquire the correct ffmpeg version, read more [here](https://github.com/alvr-org/ALVR/wiki/Build-from-source#linux-experimental-build). - -## 2021-04-22 - -The PR in the last log was proceeded by [#604](https://github.com/alvr-org/ALVR/pull/604) and this new PR was merged into the main branch. Build instructions remain the same, but the `vrenv.sh` patching is no longer needed. - -## 2021-04-01 - -A [PR](https://github.com/alvr-org/ALVR/pull/569) has been made integrating Xytovl's vulkan layer into the main ALVR tree. It doesn't actually stream video yet but it provides a solid base for future work and is compatible with nVidia GPUs. - -After you've checked the PR's branch out and [built the streamer](https://github.com/alvr-org/ALVR/wiki/Build-from-source#build-streamer), you can build and install the Vulkan layer like this: - -``` -cd alvr/server/cpp/tools/vulkan-layer -mkdir build && cd build -cmake .. -make -j -``` - -Add this line: `source "$(cat $XDG_RUNTIME_DIR/alvr_dir.txt | rev | cut -d'/' -f3- | rev)/alvr/server/cpp/tools/vulkan-layer/layer/vrenv.sh"` **before** the last one (`exec "$@"`) to `/path/to/your/SteamLibrary/steamapps/common/SteamVR/bin/vrenv.sh`. - -## 2021-03-15 - -Xytovl's branch has been merged into the main repository. The build steps are unchanged. -Work has started towards a new frame capturing method using a Vulkan debug layer. - -## 2021-03-10 - -An experimental branch is available at https://github.com/xytovl/ALVR/tree/linux-port-openvr with many limitations - -### Adopted solution - -We use SteamVR direct rendering mode on a fake screen, and capture the output of the screen. Current implementation only works for AMD (and probably Intel) open source drivers. - -### Limitations - -- audio streaming is not working -- foveated encoding is not implemented -- requires superuser access for setup -- mostly untested -- requires a free port on the graphic card -- TCP streaming seems not to be working -- position is stuttering -- only supports open source drivers - -### Setup - -See [build from source](Build-from-source) - -## Usage - -Run `build/alvr_streamer_linux/ALVR Dashboard` - -On first setup, SteamVR will probably show the VR display on your screen, with the configuration window. If you have dual screen, you can move the configuration window to a visible area (with Alt + drag on most desktop environments). - -In the setup, deactivate audio streaming, switch connection to UDP, and deactivate foveated encoding. - -On the headset, launch the application, then click trust on the configuration window, which will quit. - -The headset says that the streamer will restart, but it will not. You must relaunch it manually. - -If you are here, once it is all restarted, you should be able to get the stream on the headset. - -## 2021-01-15 - -The development road has been defined, but we are not completely sure everything will work. - -* We can try to extract frames from the VR game using a custom Vulkan validation layer. Examples are: - * Vulkan tools screenshot: https://github.com/LunarG/VulkanTools/blob/master/layersvt/screenshot.cpp - * RenderDoc: https://github.com/baldurk/renderdoc -* For the compositor (layering, color correction and foveated rendering) we are going to use Vulkan as the underlying API. We can use the backend agnostic library gfx-hal, that supports Vulkan and DirectX. Reference: https://github.com/gfx-rs/gfx -* For the encoder we can use FFmpeg. FFmpeg's hardware acceleration API supports passing pointers to GPU memory buffers directly. FFmpeg supports various acceleration APIs (hardware agnostic or not) but to minimize the effort we can go with Vulkan for Linux and DirectX 11 for Windows. Reference: https://ffmpeg.org/doxygen/trunk/hwcontext_8h.html -* For audio we are going to use the Rust library CPAL, which is an audio backend abstraction layer. We can switch (maybe even at runtime) between ALSA and JACK. CPAL supports also Windows (WASAPI, ASIO), Android (OpenSL, AAudio), Web (Emscripten) and even macOS (Core Audio) if we need that in the future. Reference: https://github.com/RustAudio/cpal - -## Earlier - -We cannot find a way of obtaining the frames rendered by the VR game from SteamVR. The OpenVR API exposes methods to do this but they don't work on Linux (at least we were not able to make them work). The two methods to obtain frames with OpenVR are by implementing the interfaces `IVRVirtualDisplay` and `IVRDriverDirectModeComponent`. On Windows, ALVR uses `IVRDriverDirectModeComponent`. On Linux, `IVRVirtualDisplay` crashes on Nvidia GPUs and does nothing on AMD. Similarly `IVRDriverDirectModeComponent` does not work on Linux. We tried to get help from Valve through multiple channels but we were not successful. - -References: - -* OpenVR driver header: https://github.com/ValveSoftware/openvr/blob/master/headers/openvr_driver.h -* Main OpenVR issue tracker: https://github.com/ValveSoftware/openvr/issues -* Virtual display sample issue tracker: https://github.com/ValveSoftware/virtual_display/issues -* Linux SteamVR issue tracker: https://github.com/ValveSoftware/steam-for-linux/issues diff --git a/wiki/Linux-Troubleshooting.md b/wiki/Linux-Troubleshooting.md index 95836b75c6..74bbb2c727 100644 --- a/wiki/Linux-Troubleshooting.md +++ b/wiki/Linux-Troubleshooting.md @@ -1,10 +1,12 @@ # Linux Troubleshooting ## SteamVR + The steam runtimes SteamVR runs in break the alvr driver loaded by SteamVR. This causes the screen to stay black on the client or an error to be reported that the pipewire device is missing or can even result in SteamVR crashing. ### Fix + Add `~/.local/share/Steam/steamapps/common/SteamVR/bin/vrmonitor.sh %command%` to the commandline options of SteamVR (SteamVR -> Manage/Right Click -> Properties -> General -> Launch Options). This path might differ based on your Steam installation, in that case SteamVR will not start at all. If this is the case you can figure out the actual path by going to Steam Settings -> Storage. @@ -12,43 +14,74 @@ Then pick the storage location with the star emoji (⭐) and take the path direc Finally put this entire path into the SteamVR commandline options instead of the other one. ## Amdvlk/AMD + If you have Amdvlk installed on your system, it overrides other vulkan drivers and causes SteamVR to break. Use the `vulkan-radeon` driver (aka radv) instead. ### Fix + Check if Amdvlk is installed by seeing if `ls /usr/share/vulkan/icd.d/ | grep amd_icd` shows anything. If so, uninstall Amdvlk from your system. ## Nvidia + Alvr requires at least driver version 535 and CUDA version 12.1. If this is not the case SteamVR or the encoder might not work. ### Fix + Install at least the required versions of the driver and ensure you have CUDA installed with at least version 12.1. If an error saying CUDA was not detected persists, try using the latest alvr nightly release. ## Hybrid graphics + ### Amd/Intel integrated gpu + Amd/Intel discrete gpu + Put `DRI_PRIME=1 %command%` into SteamVR's commandline options and in those of all VR games you intend to play with ALVR. ### Amd/Intel integrated gpu + Nvidia discrete gpu + Put `__NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia %command%` into SteamVR's commandline options and in those of all VR games you intend to play with ALVR. ## Wayland + When using hyprland or Gnome Wayland you need to put `WAYLAND_DISPLAY='' %command%` into the SteamVR commandline options to force XWayland. ## SlimeVR + The view shakes. ### Fix + Start the SlimeVR Server only after you connected and got an image to alvr at least once. ## 109 Error + The 109 error or others appear. ### Fix + Start Steam first before starting SteamVR through alvr. If SteamVR is already started, restart it. +## No audio or microphone + +Even though audio or microphone are enabled in presets, neither seems to appear in devices list + +### Fix + +Check if you have `pipewire` installed and it's at least version `0.3.49` by using command `pipewire --version` +For older (<=22.04 or debian <=11) ubuntu or debian based distributions you can check [pipewire-upstream](https://github.com/pipewire-debian/pipewire-debian) page for installing newer pipewire version + ## Arch AUR + The alvr driver doesn't get detected by SteamVR. ### Fix + Try using a portable .tar.gz release from the Releases page. + +## Low AMDGPU performance and shutters + +This might be caused by [[PERF] Subpar GPU performance due to wrong power profile mode · Issue #469 · ValveSoftware/SteamVR-for-Linux · GitHub](https://github.com/ValveSoftware/SteamVR-for-Linux/issues/469). + +### Fix + +Using CoreCtrl is highly advised (install it using your distribution package management) and in settings set your GPU to VR profile, as well as cpu to performance profile (if it's old Ryzen cpu). \ No newline at end of file diff --git a/wiki/My-game-is-not-working-properly!-Help!.md b/wiki/My-game-is-not-working-properly!-Help!.md index 843045eba8..0af691633c 100644 --- a/wiki/My-game-is-not-working-properly!-Help!.md +++ b/wiki/My-game-is-not-working-properly!-Help!.md @@ -10,6 +10,6 @@ Most of the time its the overly specific initialization of the game towards a sp For example, Vivecraft broke because ALVR reported the headset manufacturer as "Oculus driver 1.38.0" and not as "Oculus". In general, this is a rather bad practice as all relevant data can be accessed trough SteamVR and the game should not make assumptions based on the manufacturer of the hmd. There are many different fields that a game could require to run. -Nonetheless, we want to play and support those games. +Nonetheless, we want to play and support those games. Problem is, that we don't own all games. This is a Open Source without any funding. We can not buy any games just to fix a bug. In the case of Vivecraft, one user (thanks @Avencore) was generous to gift us a copy and the bug could be fixed. There are no guaranties! Neither on the time it will take nor if the bug will ever be fixed! Please contact us before buying anything. diff --git a/wiki/Real-time-video-upscaling-experiments.md b/wiki/Real-time-video-upscaling-experiments.md index e23034207c..1e0ce23897 100644 --- a/wiki/Real-time-video-upscaling-experiments.md +++ b/wiki/Real-time-video-upscaling-experiments.md @@ -1,6 +1,6 @@ # Real time video upscaling experiments -# Why? +## Why? The Quest can display a resolution close to 4k. Rendering a game, encoding and decoding these kinds of resolutions is very taxing on both the PC and the Quest. So usually a lower resolution image displayed on the Quest. @@ -8,19 +8,19 @@ Ideally the output of such an upscaled image should match the screens pixels 1:1 Currently ALVR does no upscaling prior to the image being mapped to an OpenGL texture. This texture gets interpolated to match the screen pixels by OVR. For this process video resolutions above 100% it use bilinear interpolation and for resolutions below 100% it uses nearest neighbor. -There's a lot of good info on this topic in this issue: https://github.com/alvr-org/ALVR/issues/39 +There's a lot of good info on this topic in this issue: -# Lanczos resampling +## Lanczos resampling This traditional upscaling method seems like a good step up from basic bilinear interpolation and is relatively light on GPU resources. -A GPL 2 implementation of a Lanczos shader can be found here: https://github.com/obsproject/obs-studio/blob/6943d9a973aa3dc935b39f99d06f4540ea79da61/libobs/data/lanczos_scale.effect +A GPL 2 implementation of a Lanczos shader can be found here: -# Neural net image super resolution +## Neural net image super resolution I did some basic investigations on the feasibility of using AI upscalers to get even better results than traditional signal processing methods. -## Hardware acceleration on the XR2 +### Hardware acceleration on the XR2 There seem to be 3 paths towards getting fast NNs running on the Quest's SoC. There is the [Qualcomm Neural Processing SDK](https://developer.qualcomm.com/software/qualcomm-neural-processing-sdk/tools), which automatically detects what the capabilities of the system are and picks the right hardware to run the NN on (GPU, DSP, AI accelerator). @@ -31,7 +31,7 @@ Then there is also the [TensorFlow Lite Hexagon delegate](https://www.tensorflow I only tested an example image super-resolution app from the [tensorflow respository](https://github.com/tensorflow/examples/tree/master/lite/examples/super_resolution) in CPU and generic GPU (OpenCL) accelerated modes. Even upscaling tiny 50x50 images took around 500ms with this. Even though better hardware acceleration could improve this I do not expect 100x improvements. The only hope for NN super-resolution to be viable would be to find a significantly faster neural net, which leads us into the next topic. -## Existing neural nets +### Existing neural nets A well established real-time upscaler is [Anime4K](https://github.com/bloc97/Anime4K/). It states that it can achieve 1080p to 2160p upscaling in 3ms on a Vega64 GPU. A [rough estimate](https://uploadvr.com/oculus-quest-2-benchmarks/) puts the Quest 2 at a 10x performance disadvantage compared to such high end desktop GPUs. It doesn't seem entirely impossible to get this to work with some optimizations and lowering of the upscaling quality, but there is more bad news. Anime4K has a rather bad Peak signal-to-noise ratio (PSNR). It can get away with this because the stylized look anime is quite forgiving in being heavily filtered. diff --git a/wiki/Roadmap.md b/wiki/Roadmap.md index 09d3b816a5..2d332158c3 100644 --- a/wiki/Roadmap.md +++ b/wiki/Roadmap.md @@ -4,7 +4,7 @@ This post will continue to evolve during ALVR development. ## Long-term goal -Create a universal bridge between XR devices. +Create a universal bridge between XR devices. ## What is coming next diff --git a/wiki/Settings-tutorial.md b/wiki/Settings-tutorial.md index d32bd9130b..9bdf82b3d7 100644 --- a/wiki/Settings-tutorial.md +++ b/wiki/Settings-tutorial.md @@ -1,6 +1,6 @@ # Settings tutorial -Written as of 2023/02, should be applicable to ALVR v19 and v20. +Applicable to ALVR v20. This tutorial will help you find optimal settings for your hardware and network as well as give you some pointers to troubleshoot common configuration issues. @@ -8,17 +8,19 @@ as well as give you some pointers to troubleshoot common configuration issues. ## Prerequisites * You have installed the ALVR streamer on your PC, and the ALVR client on your HMD. -* You can reach the SteamVR void (or the SteamVR home) and are able to launch games. +* You can launch up to the SteamVR void (or up to the SteamVR home) and are able to launch games. ## Step 1: choose resolution, refresh rate, codec -To get a sharp image, the HMD's native resolution should be used. In ALVR, use the "absolute" setting and type in your HMD's native resolution. For example, the Quest 2 has 1832x1920 pixels per eye, so use a width of 3664 and a height of 1920. Individual games can still have their render scale changed from within the SteamVR overlay setting. +To get a sharp image, you need combination of high resolution, enough sharpening, good bitrate with chosen codec. +For example, on good wireless router you can use medium resolution preset (default) with 1.0 sharpening (higher than default) with H.264 set at constant 400-500 mbps, or hevc at costant 100-150 mbps. In wired case, you can go all the way to 800-1000 mbps constant bitrate on H.264. Next, choose a refresh rate. Obviously higher is better, but on weaker/older hardware it's often preferable to use a lower setting that gives consistent results. For the Quest 2, 120 Hz has to be enabled in its settings. A few notes on codec choices: -* HEVC/H.265 is usually best on AMD hardware, and in bitrate constrained scenarios. +* AV1 works only on latest gen gpus (Nvidia RTX 4xxx and AMD Radeon RX 7xxx) and on Quest 3 only. +* HEVC/H.265 is usually best for bitrate cosntained scenarious. * AVC/H.264 (with CAVLC) may save a few milliseconds of decode latency, but needs a much higher bitrate to reach similar image quality. * Software encoding (x264) can give good results on a beefy high core-count CPU and a very high bitrate. Will require playing with a USB3 cable. The only choice if you don't have a hardware encoder (eg, RX6500). diff --git a/wiki/Troubleshooting.md b/wiki/Troubleshooting.md index 86efd61806..62c6b5410f 100644 --- a/wiki/Troubleshooting.md +++ b/wiki/Troubleshooting.md @@ -25,14 +25,14 @@ Trouble starting ALVR ALVR needs a working graphics driver to be installed in order to work. -**On linux**, you also need to make sure you have `x264` for base software encoding to work and either `vaapi` on AMD or `cuda` on NVIDIA for hardware encoders to work. +**On linux**, you also need to make sure you have either `vaapi` on AMD or `cuda` on NVIDIA for hardware encoders to work. ALVR starts launching, but gets stuck on "ALVR is not responding..." === With ALVR versions >= 20.0, some antivirus software can prevent ALVR from launching SteamVR. Try disabling any antivirus other than Windows Defender (McAfee, Norton, etc.), reboot, then try again. If the issue persists, make sure you don't have an instance of ALVR or SteamVR running in the background (check in Task Manager). If you continue having issues, hop in the [ALVR Discord server](https://discord.gg/KbKk3UM), and we'll do our best to help you get it sorted out. -ALVR starts fine, but... +ALVR starts fine, but === This section has some advice for when ALVR shows an error (or sometimes warning) pop-up. This could be either a yellow pop-up in the setup window (`ALVR Dashboard.exe`) or a separate pop-up when you connect with a headset. @@ -46,7 +46,7 @@ The latest release can be found [here](https://github.com/alvr-org/ALVR/releases The version of ALVR available on the SideQuest store is compatible with the latest release on GitHub (the previous link). Keep in mind that the version on SideQuest might take us a while to update after a new version is released on GitHub. -Failed to initialize CEncoder. +Failed to initialize CEncoder --- ALVR currently needs a recent AMD or Nvidia GPU to run, since it utilizes hardware video encoding (see [requirements](https://github.com/alvr-org/ALVR#requirements)). If you get an error saying something like @@ -83,7 +83,7 @@ ALVR client list is empty ![Empty ALVR client list](images/ALVRexe-no-clients.png) -Check that the PC app and the headset app run on the latest version of ALVR. At the time of writing, the latest version is v20.4.3. If your version is v2.3.1 or v2.4.0-alpha5 then you downloaded ALVR from the wrong link. The correct link is https://github.com/alvr-org/ALVR. +Check that the PC app and the headset app run on the latest version of ALVR. If your version is v2.3.1 or v2.4.0-alpha5 then you downloaded ALVR from the wrong link. The correct link is . Make sure ALVR is running both on the PC and on the headset. To be visible in the client list, ALVR on the headset sends broadcast packets which the PC application listens for. These can be blocked by your firewall or possibly your router, if both headset and PC are connected wirelessly, having AP isolation enabled on the router will cause this. @@ -112,7 +112,7 @@ Check that SteamVR isn't blocking ALVR (see SteamVR settings, enable advanced se If you're still getting this message (or otherwise not getting a headset icon in the SteamVR window), a SteamVR log (vrserver.txt) will have some information on why the driver isn't loading. You can find it where you installed Steam, in `Steam\logs\vrserver.txt`. -#### Some lines to look for and tips for them: +### Some lines to look for and tips for them `Unable to load driver alvr_server because of error VRInitError_Init_FileNotFound(103). Skipping.` - This usually means a library that ALVR needs is missing. Make sure you followed installation instructions carefully, installed the latest Visual C++ Redistributable x64 package and no files are missing where you extracted ALVR (especially in the bin\win64 directory). diff --git a/wiki/Use-ALVR-through-a-USB-connection.md b/wiki/Use-ALVR-through-a-USB-connection.md index 98b5a63b67..68bbc3cd34 100644 --- a/wiki/Use-ALVR-through-a-USB-connection.md +++ b/wiki/Use-ALVR-through-a-USB-connection.md @@ -1,6 +1,6 @@ # ALVR wired setup (ALVR over USB) -## ALVR Streamer (PC) Configuration: +## ALVR Streamer (PC) Configuration * **Switch the connection streaming protocol to TCP** in Settings > Connection. * If your headset is detected, click "Trust." Click "Edit", "Add new" and change the IP address to `127.0.0.1`. @@ -12,7 +12,7 @@ The Quest, Pico HMDs are Android devices, therefore, we can use [Android Device You can accomplish this with some pre-made applications/scripts (just below), or run the commands manually with [SideQuest](https://sidequestvr.com/setup-howto) -If you haven't already, connect a USB cable from your PC to your headset. USB 2.0 will work fine but 3.0 and higher is best. +If you haven't already, connect a USB cable from your PC to your headset. USB 2.0 will work fine but 3.0 and higher is best. **Make sure to enable dev account and authorize the computer in your headset if you're on quest or enable USB Debug on Pico in settings.** @@ -26,7 +26,7 @@ The following programs serve to wrap and simplify the process of doing manual AD * Downloads ADB for you * Cross-platform (Windows & Linux) -* [**Python Script**](https://gist.github.com/Bad-At-Usernames/684784f42cbb69e22688a21173ec263d) +* [**Python Script**](https://gist.github.com/Bad-At-Usernames/684784f42cbb69e22688a21173ec263d) * Lightweight and simple * Requires [Python 3](https://www.python.org/downloads/) and [PyWin32](https://pypi.org/project/pywin32/) @@ -38,11 +38,11 @@ The following programs serve to wrap and simplify the process of doing manual AD * Requires [ADB Platform Tools](https://developer.android.com/studio/releases/platform-tools), edit the path in line 2 to point to the directory where you extracted `platform-tools` * Needs to be run every time you (re)connect your headset -### Option 2 - [SideQuest](https://sidequestvr.com/setup-howto): +### Option 2 - [SideQuest](https://sidequestvr.com/setup-howto) * Ensure SideQuest is running, and the headset has authorized the USB connection to the PC * Open the 'Run ADB Commands' menu in SideQuest (top-right, box with an arrow inside it) -* Click 'Custom Command' and run these adb commands: +* Click 'Custom Command' and run these adb commands: * `adb forward tcp:9943 tcp:9943` * `adb forward tcp:9944 tcp:9944` * These commands will need to be run every time you (re)connect your headset. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md index b5823cc3b2..ad1e563c17 100644 --- a/wiki/_Sidebar.md +++ b/wiki/_Sidebar.md @@ -1,4 +1,4 @@ -**Start here** +#### Start here * [Installation guide](https://github.com/alvr-org/ALVR/wiki/Installation-guide) @@ -8,7 +8,7 @@ *** -**Configuration** +#### Configuration * [Settings tutorial](https://github.com/alvr-org/ALVR/wiki/Settings-tutorial) @@ -22,7 +22,7 @@ *** -**Troubleshooting** +#### Troubleshooting * [Troubleshooting](https://github.com/alvr-org/ALVR/wiki/Troubleshooting) @@ -38,7 +38,7 @@ *** -**Development** +#### Development * [Roadmap](https://github.com/alvr-org/ALVR/wiki/Roadmap) From ebc749a5dee6e9eb6ec5ebae385593e4a8f51a2e Mon Sep 17 00:00:00 2001 From: plyshka Date: Sun, 7 Jul 2024 20:28:20 +0500 Subject: [PATCH 2/8] Removed unnecessary change --- alvr/dashboard/src/dashboard/components/setup_wizard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alvr/dashboard/src/dashboard/components/setup_wizard.rs b/alvr/dashboard/src/dashboard/components/setup_wizard.rs index 84babd175c..5700459b35 100644 --- a/alvr/dashboard/src/dashboard/components/setup_wizard.rs +++ b/alvr/dashboard/src/dashboard/components/setup_wizard.rs @@ -75,7 +75,7 @@ impl SetupWizard { ui.horizontal(|ui| { ui.add_space(60.0); - ui.vertical(|ui: &mut Ui| { + ui.vertical(|ui| { ui.add_space(30.0); ui.heading(RichText::new("Welcome to ALVR").size(30.0)); ui.add_space(5.0); From 1eb1ab05893f7b9eec56577bfb966659f8feff1e Mon Sep 17 00:00:00 2001 From: plyshka Date: Mon, 8 Jul 2024 12:41:18 +0500 Subject: [PATCH 3/8] PR review fixes, removed old audio script --- alvr/xtask/flatpak/audio-flatpak-setup.sh | 82 ----------------------- wiki/Installation-guide.md | 10 ++- 2 files changed, 7 insertions(+), 85 deletions(-) delete mode 100644 alvr/xtask/flatpak/audio-flatpak-setup.sh diff --git a/alvr/xtask/flatpak/audio-flatpak-setup.sh b/alvr/xtask/flatpak/audio-flatpak-setup.sh deleted file mode 100644 index 0d2f2324c4..0000000000 --- a/alvr/xtask/flatpak/audio-flatpak-setup.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -# Set either to 0 to prevent from creating audio or microphone sinks and instead you using own headset or microphone -USE_HEADSET_AUDIO=1 -USE_HEADSET_MIC=1 - -function get_playback_sink_input_id() { - get_playback_id sink-inputs 'Sink Input' "$1" -} - -function get_playback_source_output_id() { - get_playback_id source-outputs 'Source Output' "$1" -} - -function get_playback_id() { - local last_node_name='' - local last_node_id='' - pactl list "$1" | while read -r line; do - node_id=$(echo "$line" | grep -oP "$2 #\K.+" | sed -e 's/^[ \t]*//') - node_name=$(echo "$line" | grep -oP 'node.name = "\K[^"]+' | sed -e 's/^[ \t]*//') - if [[ "$node_id" != '' ]] && [[ "$last_node_id" != "$node_id" ]]; then - last_node_id="$node_id" - fi - if [[ -n "$node_name" ]] && [[ "$last_node_name" != "$node_name" ]]; then - last_node_name="$node_name" - if [[ "$last_node_name" == "$3" ]]; then - echo "$last_node_id" - return - fi - fi - done -} - -function get_sink_id_by_name() { - local sink_name - sink_name=$1 - pactl list short sinks | grep "$sink_name" | cut -d$'\t' -f1 -} - -function setup_mic() { - if [[ $USE_HEADSET_MIC == 1 ]]; then - echo "Creating microphone sink & source and linking alvr playback to it" - # This sink is required so that it persistently auto-connects to alvr playback later - pactl load-module module-null-sink sink_name=ALVR-MIC-Sink media.class=Audio/Sink | tee -a /run/user/1000/alvr-audio - # This source is required so that any app can use it as microphone - pactl load-module module-null-sink sink_name=ALVR-MIC-Source media.class=Audio/Source/Virtual | tee -a /run/user/1000/alvr-audio - # We link them together - pw-link ALVR-MIC-Sink:monitor_FL ALVR-MIC-Source:input_FL - pw-link ALVR-MIC-Sink:monitor_FR ALVR-MIC-Source:input_FR - # And we assign playback of pipewire alsa playback to created alvr sink - pactl move-sink-input "$(get_playback_sink_input_id 'ALSA plug-in [vrserver]')" "$(get_sink_id_by_name ALVR-MIC-Sink)" - pactl set-default-source ALVR-MIC-Source - fi -} - -function unload_modules() { - echo "Unloading audio, microphone sink & source" - while read -r line; do - pactl unload-module "$line" - done <"/run/user/1000/alvr-audio" - >/run/user/1000/alvr-audio -} - -function setup_audio() { - if [[ $USE_HEADSET_AUDIO == 1 ]]; then - echo "Setting up audio" - pactl load-module module-null-sink sink_name=ALVR-AUDIO-Sink media.class=Audio/Sink | tee -a /run/user/1000/alvr-audio - pactl set-default-sink ALVR-AUDIO-Sink - pactl move-source-output "$(get_playback_source_output_id 'ALSA plug-in [vrserver]')" "$(get_sink_id_by_name ALVR-AUDIO-Sink)" - fi -} - -case $ACTION in -connect) - unload_modules - setup_audio - setup_mic - ;; -disconnect) - unload_modules - ;; -esac diff --git a/wiki/Installation-guide.md b/wiki/Installation-guide.md index b51e9db846..abc6ce5982 100644 --- a/wiki/Installation-guide.md +++ b/wiki/Installation-guide.md @@ -6,7 +6,7 @@ Launcher will allow you to manage old, current and new installations of ALVR str ### Installation -* Download `alvr_launcher_windows.zip` on windows or `alvr_launcher_linux.tar.gz` from the release [download page](https://github.com/alvr-org/ALVR/releases/latest) and extract into a path that contains only ASCII characters (english only) and has edit permissions without administrator or root rights. +* Download `alvr_launcher_windows.zip` (on Windows) or `alvr_launcher_linux.tar.gz` (on Linux) from the release [download page](https://github.com/alvr-org/ALVR/releases/latest) and extract into a path that contains only ASCII characters (english only) and has edit permissions without administrator or root rights. * Run `ALVR Launcher.exe` (on Windows) or `alvr_launcher_linux/ALVR Launcher` (on Linux) * Press `Add version` button * For default installation keep channel and version as is and press `Install` @@ -16,7 +16,7 @@ Launcher will allow you to manage old, current and new installations of ALVR str ### Usage -* Before launching SteamVR through ALVR, please install it and run at least once without ALVR and close it. +* Before launching SteamVR through ALVR, please install it. First time launch will result in steamvr being blank and alvr will not work - close it and start again. It will have registered driver and should work. * Launch ALVR client on your headset. While the headset screen is on, click `Trust` next to the client entry (in the ALVR streamer app on PC, in the `Devices` tab) to start streaming. * You can change settings on the PC in the `Settings` tab. Most of the settings require to restart SteamVR to be applied. Use the apposite button on the bottom right corner. @@ -77,7 +77,11 @@ By default ALVR disables other SteamVR drivers before starting. Among these driv ### Launch ALVR together with SteamVR -You can skip the ALVR Dashboard and open ALVR automatically together with SteamVR. Open ALVR, go to the `Installation` tab and click on `Register ALVR driver`. +You can skip the ALVR Dashboard and open ALVR automatically together with SteamVR. + +**Note:** You can only do that while SteamVR is not already running. Otherwise driver might be unregistered on shutdown. + +Open ALVR, go to the `Installation` tab and click on `Register ALVR driver`. ### Connect headset to PC via a USB Cable From 630ed0d0ed5e47b847dc12da23d8f20db505c35c Mon Sep 17 00:00:00 2001 From: plyshka Date: Mon, 8 Jul 2024 12:42:38 +0500 Subject: [PATCH 4/8] Small change in separate networks on uix --- wiki/ALVR-client-and-streamer-on-separate-networks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wiki/ALVR-client-and-streamer-on-separate-networks.md b/wiki/ALVR-client-and-streamer-on-separate-networks.md index 7615d98b72..7dd69e6373 100644 --- a/wiki/ALVR-client-and-streamer-on-separate-networks.md +++ b/wiki/ALVR-client-and-streamer-on-separate-networks.md @@ -6,7 +6,7 @@ Here are explained two methods to connect PC and headset remotely, port-forwardi ## Important notes on security -* ALVR protocol does not have any encryption or authentication (apart from ALVR client IP address shown in ALVR streamer and the requirement to click _Connect_ on ALVR streamer). +* ALVR protocol does not have any encryption or authentication (apart from ALVR client IP address shown in ALVR streamer and the requirement to add client on ALVR streamer). * It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR clients should be allowed) and ALVR streamer should not be left running unattended. * **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR client could take over the PC**). * As the license states ALVR IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND (see the file `LICENSE` in this GitHub repository for legal text/definition). You are on your own (especially if you run ALVR over the Internet without VPN). From 362cb1be5b0ad000e3c70b6c5e67727412807374 Mon Sep 17 00:00:00 2001 From: Leonhard Saam Date: Wed, 10 Jul 2024 09:04:13 +0200 Subject: [PATCH 5/8] docs: rename client to device --- ...lient-and-streamer-on-separate-networks.md | 8 +- wiki/Building-From-Source.md | 10 +-- ...uration-Information-and-Recommendations.md | 2 +- wiki/How-ALVR-works.md | 78 +++++++++---------- wiki/Installation-guide.md | 8 +- wiki/Linux-Troubleshooting.md | 4 +- wiki/Settings-tutorial.md | 4 +- wiki/Troubleshooting.md | 16 ++-- wiki/_Sidebar.md | 2 +- 9 files changed, 66 insertions(+), 66 deletions(-) diff --git a/wiki/ALVR-client-and-streamer-on-separate-networks.md b/wiki/ALVR-client-and-streamer-on-separate-networks.md index 7dd69e6373..82b7609681 100644 --- a/wiki/ALVR-client-and-streamer-on-separate-networks.md +++ b/wiki/ALVR-client-and-streamer-on-separate-networks.md @@ -6,9 +6,9 @@ Here are explained two methods to connect PC and headset remotely, port-forwardi ## Important notes on security -* ALVR protocol does not have any encryption or authentication (apart from ALVR client IP address shown in ALVR streamer and the requirement to add client on ALVR streamer). -* It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR clients should be allowed) and ALVR streamer should not be left running unattended. -* **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR client could take over the PC**). +* ALVR protocol does not have any encryption or authentication (apart from ALVR device IP address shown in ALVR streamer and the requirement to add devices on ALVR streamer). +* It is recommended to run ALVR via encrypted tunnel (VPN) over the internet. In case VPN is not an option, access to ALVR streamer (UDP ports 9943 and 9944) should be restricted by Windows Firewall (only connections from known IP addresses of ALVR devices should be allowed) and ALVR streamer should not be left running unattended. +* **Warning!** SteamVR allows to control desktop from VR headset (i.e. a **malicious ALVR device could take over the PC**). * As the license states ALVR IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND (see the file `LICENSE` in this GitHub repository for legal text/definition). You are on your own (especially if you run ALVR over the Internet without VPN). ## Port-forwarding @@ -23,7 +23,7 @@ Port-forwarding allows to connect devices that are behind different NATs, i.e. l You can now use ALVR to connect to your remote PC. -**Note**: The public IP can change often. Every time you want to use ALVR you need to check that your current public IP is the same as the last time. If the IP changed, you can update it using the "Configure client" interface, accessed with the `Configure` button next to your headset name on the streamer. +**Note**: The public IP can change often. Every time you want to use ALVR you need to check that your current public IP is the same as the last time. If the IP changed, you can update it using the "Edit connection" interface, accessed with the `Edit` button next to your headset name on the streamer. ## ZeroTier diff --git a/wiki/Building-From-Source.md b/wiki/Building-From-Source.md index 07f8a29858..90eb7b8bbd 100644 --- a/wiki/Building-From-Source.md +++ b/wiki/Building-From-Source.md @@ -70,7 +70,7 @@ If you want to edit and rebuild the code, you can skip the `prepare-deps` comman ## Fedora CUDA installation -If you are here for CUDA installation on Fedora you're at the right place! Else continue down to [Client Building](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#client-building) +If you are here for CUDA installation on Fedora you're at the right place! Else continue down to [Android App Building](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#android-app-building) ### 1. Install Nvidia drivers and Fedora CUDA driver @@ -145,11 +145,11 @@ From the ALVR directory edit the ./alvr/xtask/src/dependencies.rs, and change tw You should be good to go! Refer to [Streamer Building](https://github.com/alvr-org/ALVR/wiki/Building-From-Source#streamer-building) for the commands to build ALVR -# Client Building +# Android App Building ## 1. Installing necessary packages -For the client you need install: +For the app you need install: * [Android Studio](https://developer.android.com/studio) or the [sdkmanager](https://developer.android.com/studio/command-line/sdkmanager) * Android SDK Platform-Tools 29 (Android 10) @@ -224,7 +224,7 @@ Move to the root directory of the project, then run this command: cargo xtask prepare-deps --platform android ``` -Before building the client, Android has to have us to agree to the licenses otherwise building the client will halt and fail. To accept the agreements, follow the instructions for your corresponding OS: +Before building the app, Android has to have us to agree to the licenses otherwise building the app will halt and fail. To accept the agreements, follow the instructions for your corresponding OS: * Windows: @@ -240,7 +240,7 @@ Before building the client, Android has to have us to agree to the licenses othe sdkmanager --licenses ``` -Next up is the proper build of the client. Run the following: +Next up is the proper build of the app. Run the following: ```bash cargo xtask build-client --release diff --git a/wiki/Configuration-Information-and-Recommendations.md b/wiki/Configuration-Information-and-Recommendations.md index bd7578e161..6e967b5495 100644 --- a/wiki/Configuration-Information-and-Recommendations.md +++ b/wiki/Configuration-Information-and-Recommendations.md @@ -47,7 +47,7 @@ ## Routing / Switching / Firewalling / General Info -- Ideally client and streamer should exist on the same logical (layer 2) network and subnet - this allows for no routing overhead, and the correct function of client discovery via [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) +- Ideally the headset and streamer should exist on the same logical (layer 2) network and subnet - this allows for no routing overhead, and the correct function of device discovery via [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) - Twisted pair (normal copper ethernet cables) should never be run alongside power cables - this can cause signal noise and result in frame loss and lowered auto-negotiation speeds - High quality CAT5E or higher (ideally CAT6A or CAT7) gigabit+ cabling should be used for modern networks - In some cases firewall, anti-virus, malware, or EDR (enhanced detection and response) software may interfere with network traffic - Windows Defender and Sophos Endpoint Protection are reported to work without issue diff --git a/wiki/How-ALVR-works.md b/wiki/How-ALVR-works.md index 6ad5cec08f..6c9b89996c 100644 --- a/wiki/How-ALVR-works.md +++ b/wiki/How-ALVR-works.md @@ -21,11 +21,11 @@ This document was last updated on June 27th 2023 and refers to the master branch * Driver communication * Driver lifecycle * The streaming pipeline: Overview -* Client-driver communication +* Device-driver communication * Discovery * Streaming * SteamVR driver -* Client and driver compositors +* Device and driver compositors * Foveated rendering * Color correction * Video transcoding @@ -40,29 +40,29 @@ This document was last updated on June 27th 2023 and refers to the master branch ### The packaged application -ALVR is made of two applications: the streamer and client. The streamer can be installed on Windows and Linux, while the client is installed on Android VR headsets. The client communicates with the driver through TCP or UDP sockets. +ALVR is made of two applications: the streamer and device. The streamer can be installed on Windows and Linux, while the device app is installed on Android VR headsets. The device app communicates with the driver through TCP or UDP sockets. -The client is a single unified APK, named `alvr_client_android.apk`. It is powered by OpenXR and it is compatible with Quest headsets, recent Pico headsets and HTC Focus 3 and XR Elite. +The app is a single unified APK, named `alvr_client_android.apk`. It is powered by OpenXR and it is compatible with Quest headsets, recent Pico headsets and HTC Focus 3 and XR Elite. The streamer is made of two main parts: the dashboard and the driver (also known as server). The driver is dynamically loaded by SteamVR. This is the file structure on Windows: * `bin/win64/` - * `driver_alvr_server.dll`: The main binary, responsible for client discovery and streaming. Loaded by SteamVR. + * `driver_alvr_server.dll`: The main binary, responsible for device discovery and streaming. Loaded by SteamVR. * `driver_alvr_server.pdb`: Debugging symbols * `openvr_api.dll`: OpenVR SDK used for updating the chaperone. * `vcruntime140_1.dll`: Windows SDK used by C++ code in the driver. -* `ALVR Dashboad.exe`: Dashboard binary used to change settings, manage clients, monitor statistics and do installation actions. It can launch SteamVR. +* `ALVR Dashboad.exe`: Dashboard binary used to change settings, manage devices, monitor statistics and do installation actions. It can launch SteamVR. * `driver.vrdrivermanifest`: Auxiliary config file used by the driver. At runtime, some other files are created: -* `session.json`: This contains unified configuration data used by ALVR, such as settings and client records. -* `session_log.txt`: Main log file. Each line is a json structure and represents an event generated by the driver. This gets cleared each time a client connects. +* `session.json`: This contains unified configuration data used by ALVR, such as settings and device records. +* `session_log.txt`: Main log file. Each line is a json structure and represents an event generated by the driver. This gets cleared each time a device connects. * `crash_log.txt`: Auxiliary log file. Same as `session_log.txt`, except only error logs are saved, and does not get cleared. ### Programming languages -ALVR is written in multiple languages: Rust, C, C++, HLSL, GLSL. The main language used in the codebase is Rust, which is used for the dashboard, networking, video decoding and audio code. C and C++ are used for graphics, video encoding and SteamVR integration. HLSL is used for graphics shaders on the Windows driver, GLSL is used on the Linux driver and the client. Moving forward, more code will be rewritten from C/C++ to Rust and HLSL code will be moved to GLSL or WGSL. +ALVR is written in multiple languages: Rust, C, C++, HLSL, GLSL. The main language used in the codebase is Rust, which is used for the dashboard, networking, video decoding and audio code. C and C++ are used for graphics, video encoding and SteamVR integration. HLSL is used for graphics shaders on the Windows driver, GLSL is used on the Linux driver and the app. Moving forward, more code will be rewritten from C/C++ to Rust and HLSL code will be moved to GLSL or WGSL. Rust is a system programming language focused on memory safety and ease of use. It is as performant as C++ but Rust code is less likely to be affected by runtime bugs. The prime feature Rust feature used by ALVR is enums, that correspond to tagged unions in C++. Rust's enum is a data type that stores different kinds of data, but only one type can be accessed at a time. For example the type `Result` can contain either an `Ok` value or an `Err` value but not both. Together with pattern matching, this is the foundation of error management in Rust applications. @@ -72,19 +72,19 @@ ALVR code is hosted in a monorepo. This is an overview of the git tree: * `.github/`: Contains scripts used by the GitHub CI. * `alvr/`: Each subfolder is a Rust crate ("crate" means a code library or executable). - * `audio/`: Utility crate hosting audio related code shared by client and driver. - * `client_core/`: Platform agnostic code for the client. It is used as a Rust library for `alvr_client_openxr` and can also compiled to a C ABI shared library with a .h header for integration with other projects. - * `client_mock/`: Client mock implemented as a thin wrapper around `alvr_client_core`. - * `client_openxr/`: Client implementation using OpenXR, compiled to a APK binary. + * `audio/`: Utility crate hosting audio related code shared by app and driver. + * `client_core/`: Platform agnostic code for the app. It is used as a Rust library for `alvr_client_openxr` and can also compiled to a C ABI shared library with a .h header for integration with other projects. + * `client_mock/`: Device mock implemented as a thin wrapper around `alvr_client_core`. + * `client_openxr/`: App implementation using OpenXR, compiled to a APK binary. * `common/`: Some common code shared by other crates. It contains code for versioning, logging, struct primitives, and OpenXR paths. * `dashboard/`: The dashboard application. * `events/`: Utility crate hosting code related to events. * `filesystem/`: Utility crate hosting code for filesystem abstraction between Windows and Linux. - * `packets/`: Utility crate containing packet definitions for communication between client, driver and dashboard. + * `packets/`: Utility crate containing packet definitions for communication between device, driver and dashboard. * `server/`: The driver shared library loaded by SteamVR. * `server_io/`: Common functionality shared by dashboard and driver, for interaction with the host system. This allows dashboard and driver to work independently from each other. * `session/`: Utility crate related to session file and data management. - * `sockets/`: Utility crate shared by client and driver with socket and protocol implementation. + * `sockets/`: Utility crate shared by app and driver with socket and protocol implementation. * `vrcompositor_wrapper/`: Small script used on Linux to correctly load the ALVR Vulkan layer by SteamVR. * `vulkan_layer/`: Vulkan WSI layer used on Linux to work around limitations of the OpenVR API on Linux. This is mostly patchwork and hopefully will be removed in the future. * `xtask/`: Utility CLI hosting a variety of scripts for environment setting, building, and packaging ALVR. Should be called with `cargo xtask`. @@ -144,7 +144,7 @@ Log is a special kind of event: The driver logs events in JSON form to `session.json`, one per line. -Currently its use is limited, but eventually this will replace the current logging system, and logging will be built on top of the event system. The goal is to create a unified star-shaped network where each client and dashboard instance sends events to the server and the server broadcasts events to all other clients and dashboard instances. This should also unify the way the server communicates with clients and dashboards, making the dashboard just another client. +Currently its use is limited, but eventually this will replace the current logging system, and logging will be built on top of the event system. The goal is to create a unified star-shaped network where each device and dashboard instance sends events to the server and the server broadcasts events to all other devices and dashboard instances. This should also unify the way the server communicates with devices and dashboards, making the dashboard just another client. ## Session and settings @@ -153,7 +153,7 @@ ALVR uses a unified configuration file, that is `session.json`. It is generated * `"server_version"`: the current version of the streamer. It helps during a version upgrade. * `"drivers_backup"`: temporary storage for SteamVR driver paths. Used by the dashboard. * `"openvr_config"`: contains a list of settings that have been checked for a diff. It is used by C++ code inside the driver. -* `"client_connections"`: contains entries corresponding to known clients. +* `"client_connections"`: contains entries corresponding to known devices. * `"session_settings"`: all ALVR settings, laid in a tree structure. ### Procedural generation of code and UI @@ -183,7 +183,7 @@ These are the main components: TODO: Add screenshots * Sidebar: is used to select the tab for the main content page. -* Devices tab: used to trust clients or add them manually specifying the IP +* Devices tab: used to trust devices or add them manually specifying the IP * Statistics tab: shows graphs for latency and FPS and a summary page * Settings tab: settings page split between `Presets` and `All Settings`. `All Settings` are procedurally generated from a schema. `Presets` are controls that modify other settings. * Installation tab: utilities for installation: setting firewall rules, registering the driver, launching the setup wizard. @@ -201,7 +201,7 @@ The dashboard communicates with the driver in order to update its information an * `/api/events`: This endpoint is upgraded to a websocket and is used for listening to events from the driver * `/api/ping`: returns code 200 when the driver is alive. -The dashboard retains some functionality when the driver is not launched. It can manage settings, clients and perform installation actions, but clients cannot be discovered. Once The driver is launched all these actions are performed by the server, requested with the HTTP API. This mechanism ensures that there are no data races. +The dashboard retains some functionality when the driver is not launched. It can manage settings, devices and perform installation actions, but devices cannot be discovered. Once The driver is launched all these actions are performed by the server, requested with the HTTP API. This mechanism ensures that there are no data races. ### Driver lifecycle @@ -229,26 +229,26 @@ This might seem unnecessarily complicated. The reason for the first message roun The goal of ALVR is to bridge input and output of a PCVR application to a remote headset. In order to do this ALVR implements pipelines to handle input, video and audio. The tracking-video pipeline (as known as the motion-to-photon pipeline) is the most complex one and it can be summarized in the following steps: -* Poll tracking data on the client +* Poll tracking data on the device * Send tracking to the driver * Execute the PCVR game logic and render layers * Compose layers into a frame * Encode the video frame -* Send the encoded video frame to the client through the network -* Decode the video frame on the client +* Send the encoded video frame to the device through the network +* Decode the video frame on the device * Perform more compositor transformations * Submit the frame to the VR runtime * The runtime renders the frame during a vsync. -## Client-driver communication +## Device-driver communication -ALVR uses a custom protocol for client-driver communication. ALVR supports UDP and TCP transports. USB connection is supported although not as a first class feature; you can read more about it [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)). +ALVR uses a custom protocol for device-driver communication. ALVR supports UDP and TCP transports. USB connection is supported although not as a first class feature; you can read more about it [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)). ### Discovery -Usually the first step to establish a connection is discovery. When the server discovers a client it shows it in the "New devices" section in the Devices tab. The user can then trust the client and the connection is established. +Usually the first step to establish a connection is discovery. When the server discovers a device it shows it in the "New devices" section in the Devices tab. The user can then trust the device and the connection is established. -ALVR uses a UDP socket at 9943 for discovery. The client broadcasts a packet and waits for the driver to respond. It's the client that broadcasts and it's the driver that then asks for a connection: this is because of the balance in responsibility of the two peers. The client becomes the portal though a PC, that can contain sensitive data. For this reason the server has to trust the client before initiating the connection. +ALVR uses a UDP socket at 9943 for discovery. The device app broadcasts a packet and waits for the driver to respond. It's the device that broadcasts and it's the driver that then asks for a connection: this is because of the balance in responsibility of the two peers. The device becomes the portal to a PC, that can contain sensitive data. For this reason the server has to trust the device before initiating the connection. This is the layout of the discovery packet @@ -256,9 +256,9 @@ This is the layout of the discovery packet | :---------------: | :---------: | :------: | | "ALVR" + 0x0 x 12 | 8 bytes | 32 bytes | -* The prefix is used to filter packets and ensure a packet is really sent by an ALVR client -* The protocol ID is a unique version identifier calculated from the semver version of the client. If the client version is *semver-compatible* with the streamer, the protocol ID will match. -* Hostname: the hostname is a unique identifier for a client. When a client is launched for the first time, an hostname is chosen and it persists for then successive launches. It is reset when the app is upgraded or downgraded. +* The prefix is used to filter packets and ensure a packet is really sent by an ALVR device +* The protocol ID is a unique version identifier calculated from the semver version of the app. If the app version is *semver-compatible* with the streamer, the protocol ID will match. +* Hostname: the hostname is a unique identifier for a device. When a device is launched for the first time, an hostname is chosen and it persists for then successive launches. It is reset when the app is upgraded or downgraded. The format of the packet can change between major versions, but the prefix must remain unchanged, and the protocol ID must be 8 bytes. @@ -266,13 +266,13 @@ The format of the packet can change between major versions, but the prefix must ALVR uses two sockets for streaming: the control socket and stream socket. Currently these are implemented with async code; there's a plan to move this back to sync code. -The control socket uses the TCP transport; it is used to exchange small messages between client and server, ALVR requires TCP to ensure reliability. +The control socket uses the TCP transport; it is used to exchange small messages between device and server, ALVR requires TCP to ensure reliability. The stream socket can use UDP or TCP; it is used to send large packets and/or packets that do not require reliability, ALVR is robust to packet losses and packet reordering. The specific packet format used over the network is not clearly defined since ALVR uses multiple abstraction layers to manipulate the data (bincode, tokio Length Delimited Coding). Furthermore, packets are broken up into shards to ensure they can support the MTU when using UDP. -Since the amount of data streamed is large, the socket buffer size is increased both on the driver side and on the client. +Since the amount of data streamed is large, the socket buffer size is increased both on the driver side and on the device. ## SteamVR driver @@ -280,17 +280,17 @@ The driver is the component responsible for most of the streamer functionality. Using the OpenVR API, ALVR pushes tracking and button data to SteamVR using `vr::VRServerDriverHost()->TrackedDevicePoseUpdated()`. SteamVR then returns a rendered game frame with associated pose used for rendering. On Windows, frames are retrieved implementing the `IVRDriverDirectModeComponent` interface: SteamVR calls `IVRDriverDirectModeComponent::Present()`. On Linux this API doesn't work, and so ALVR uses a WSI Vulkan layer to intercept display driver calls done by vrcompositor. The pose associated to the frame is obtained from the vrcompositor execution stack with the help of libunwind. -## Client and driver compositors +## Device and driver compositors ALVR is essentially a bridge between PC and headset that transmits tracking, audio and video. But it also implements some additional features to improve image quality and streaming performance. To this goal, ALVR implements Fixed Foveated Rendering (FFR) and color correction. -The client compositor is implemented in OpenGL, while on the server it's either implemented with DirectX 11 on Windows or Vulkan on Linux. There are plans to move all compositor code to the graphics abstraction layer [wgpu](https://github.com/gfx-rs/wgpu), mainly for unifying the codebase. +The app compositor is implemented in OpenGL, while on the server it's either implemented with DirectX 11 on Windows or Vulkan on Linux. There are plans to move all compositor code to the graphics abstraction layer [wgpu](https://github.com/gfx-rs/wgpu), mainly for unifying the codebase. It's important to note that ALVR's compositors are separate from the headset runtime compositor and SteamVR compositors. The headset runtime compositor is part of the headset operative system and controls compositing between different apps and overlays, and prepares the image for display (with lens distortion correction, chroma aberration correction, mura and ghosting correction). On the driver side, on Windows ALVR takes responsibility for compositing layers returned by SteamVR. The only responsibility of SteamVR is converting the frame into a valid DXGI texture if the game uses OpenGL or Vulkan graphics. On Linux ALVR grabs Vulkan frames that are already composited by vrcompostor. This introduced additional challenges since vrcompositor implements async reprojection which disrupts our head tracking mechanism. ### Foveated encoding -Foveated rendering is a technique where frame images are individually compressed in a way that the human eye barely detects the compression. Particularly, the center of the image is kept at original resolution, and the rest is compressed. ALVR refers to foveated rendering as "Foveated encoding" to clarify its scope. In native standalone or PCVR apps, foveated rendering reduces the load on the GPU by rendering parts of the image ar lower resolution. In ALVR's case frames are still rendered at full resolution, but are then "encoded" (compressing the outskirts of the image) before actually encoding and transmitting them. The image is then reexpanded on the client side after decoding and before display. +Foveated rendering is a technique where frame images are individually compressed in a way that the human eye barely detects the compression. Particularly, the center of the image is kept at original resolution, and the rest is compressed. ALVR refers to foveated rendering as "Foveated encoding" to clarify its scope. In native standalone or PCVR apps, foveated rendering reduces the load on the GPU by rendering parts of the image ar lower resolution. In ALVR's case frames are still rendered at full resolution, but are then "encoded" (compressing the outskirts of the image) before actually encoding and transmitting them. The image is then reexpanded on the device side after decoding and before display. Currently ALVR supports only fixed foveation, but support for tracked eye foveation is planned. @@ -302,15 +302,15 @@ Color correction is implemented on the server and adds simple brightness, contra ## Video transcoding -To be able to send frames from driver to client through the network, they have to be compressed since current WiFi technology doesn't allow to send the amount of data of raw frames. Doing a quick conservative calculation, let's say we have 2 x 2048x2048 eye images, 3 color channels, 8 bits per channel, sent 72 times per second, that would amount to almost 15 Gbps. +To be able to send frames from driver to the device through the network, they have to be compressed since current WiFi technology doesn't allow to send the amount of data of raw frames. Doing a quick conservative calculation, let's say we have 2 x 2048x2048 eye images, 3 color channels, 8 bits per channel, sent 72 times per second, that would amount to almost 15 Gbps. -ALVR uses h264 and HEVC video codecs for compression. These codecs are chosen since they have hardware decoding support on Android and generally hardware encoding support on the PC side. On Windows, the driver uses NvEnc for Nvidia GPUs and AMF for AMD GPUS; on Linux ALVR supports VAAPI, NvEnc and AMF through FFmpeg. In case the GPU doesn't support hardware encoding, on both Windows and Linux ALVR supports software encoding with x264 (through FFmpeg), although the performance is often insufficient for a smooth experience. The client supports only MediaCodec, which is the API to access hardware video codecs on Android. +ALVR uses h264 and HEVC video codecs for compression. These codecs are chosen since they have hardware decoding support on Android and generally hardware encoding support on the PC side. On Windows, the driver uses NvEnc for Nvidia GPUs and AMF for AMD GPUS; on Linux ALVR supports VAAPI, NvEnc and AMF through FFmpeg. In case the GPU doesn't support hardware encoding, on both Windows and Linux ALVR supports software encoding with x264 (through FFmpeg), although the performance is often insufficient for a smooth experience. The app supports only MediaCodec, which is the API to access hardware video codecs on Android. h264 and HEVC codecs compression works on the assumption that consecutive frames are similar to each other. Each frame is reconstructed from past frames + some small additional data. For this reason, packet losses may cause glitches that persist many frames after the missing frame. When ALVR detects packet losses, it requests a new IDR frame from the encoder. A IDR frame is a packet that contains all the information to build a whole frame by itself; the encoder will ensure that successive frames will not rely on older frames than the last requested IDR. ## Audio -Game audio is captured on the PC and sent to the client, and microphone audio is captured on the client and sent to the PC. Windows and Linux implementation once again differ. On Windows, game audio is captured from a loopback device; microphone is is sent to virtual audio cable software to expose audio data from a (virtual) input device. On Linux the microphone does not work out-of-the-box, but there is a bash script available for creating and plugging into pipewire audio devices. +Game audio is captured on the PC and sent to the device, and microphone audio is captured on the device and sent to the PC. Windows and Linux implementation once again differ. On Windows, game audio is captured from a loopback device; microphone is is sent to virtual audio cable software to expose audio data from a (virtual) input device. On Linux the microphone does not work out-of-the-box, but there is a bash script available for creating and plugging into pipewire audio devices. Unlike for video, audio is sent as a raw PCM waveform and new packets do not rely on old packets. But packet losses may still cause popping, which happens when there is a sudden jump in the waveform. To mitigate this, when ALVR detects a packet loss (or a buffer overflow or underflow) it will render a fadeout or cross-fade. @@ -326,7 +326,7 @@ On the streamer side, ALVR needs to workaround a OpenVR API limitation. SteamVR ## Other streams -There are some other kinds of data which can be streamed without requiring any special timing. These are button presses and haptics, respectively sent from client to driver and from driver to client. +There are some other kinds of data which can be streamed without requiring any special timing. These are button presses and haptics, respectively sent from the device to the driver and from the driver to the device. ## Upcoming @@ -336,7 +336,7 @@ Phase sync is not a single algorithm but many that share similar objectives, red In general, a phase sync algorithm is composed of two parts: a queue that holds data resources or pointers, and a statistical model to predict event times. The statistical model is fed with duration or other kinds of timing samples and as output it returns a refined time prediction for a recurring event. The statistical model could be simple and just aim for a average-controlled event, or more complex that aims for submitting for a deadline; the second case needs to take into account the variance of the timing samples. Unlike Oculus implementation, these statistical models can be highly configurable to tune the target mean or target variance. -There are a few phase sync algorithms planned to be implemented: frame submission timing (to reduce frame queueing on the client, controlled by shifting the phase of the driver rendering cycle), SteamVR tracking submission timing (to make sure SteamVR is using exactly the tracking sample we want) and tracking poll timing (to reduce queuing on the server side). +There are a few phase sync algorithms planned to be implemented: frame submission timing (to reduce frame queueing on the headset, controlled by shifting the phase of the driver rendering cycle), SteamVR tracking submission timing (to make sure SteamVR is using exactly the tracking sample we want) and tracking poll timing (to reduce queuing on the server side). ## Sliced encoding diff --git a/wiki/Installation-guide.md b/wiki/Installation-guide.md index abc6ce5982..3b5b577115 100644 --- a/wiki/Installation-guide.md +++ b/wiki/Installation-guide.md @@ -2,7 +2,7 @@ ## Launcher (BETA) -Launcher will allow you to manage old, current and new installations of ALVR streamer and allow to automatically install and upgrade to specific ALVR client version on headset +Launcher will allow you to manage old, current and new installations of ALVR streamer and allow to automatically install and upgrade to specific ALVR app version on headset ### Installation @@ -11,13 +11,13 @@ Launcher will allow you to manage old, current and new installations of ALVR str * Press `Add version` button * For default installation keep channel and version as is and press `Install` * Wait until it finishes downloading, installing (depends on your connection) -* To install ALVR client on headset, use button `Install APK` +* To install ALVR app on headset, use button `Install APK` * In the list, to open streamer app (PC) press `Launch`. You will be greeted with a setup wizard. Follow the setup to set the firewall rules and other settings. ### Usage * Before launching SteamVR through ALVR, please install it. First time launch will result in steamvr being blank and alvr will not work - close it and start again. It will have registered driver and should work. -* Launch ALVR client on your headset. While the headset screen is on, click `Trust` next to the client entry (in the ALVR streamer app on PC, in the `Devices` tab) to start streaming. +* Launch ALVR app on your headset. While the headset screen is on, click `Trust` next to the device entry (in the ALVR streamer app on PC, in the `Devices` tab) to start streaming. * You can change settings on the PC in the `Settings` tab. Most of the settings require to restart SteamVR to be applied. Use the apposite button on the bottom right corner. For any problem visit the [Troubleshooting page](https://github.com/alvr-org/ALVR/wiki/Troubleshooting). @@ -30,7 +30,7 @@ Then you can enable the microphone in the ALVR setting, leave "Virtual microphon ## Advanced installation -### Installing client using Sidequest +### Installing app using Sidequest * Install SideQuest on your PC and enable developer mode on the headset. You can follow [this guide](https://sidequestvr.com/setup-howto). * Connect your headset to Sidequest. If you have Quest, Pico, and other compatible device download the ALVR app [here](https://sidequestvr.com/app/9) diff --git a/wiki/Linux-Troubleshooting.md b/wiki/Linux-Troubleshooting.md index 74bbb2c727..8dedd8d0cd 100644 --- a/wiki/Linux-Troubleshooting.md +++ b/wiki/Linux-Troubleshooting.md @@ -3,7 +3,7 @@ ## SteamVR The steam runtimes SteamVR runs in break the alvr driver loaded by SteamVR. -This causes the screen to stay black on the client or an error to be reported that the pipewire device is missing or can even result in SteamVR crashing. +This causes the screen to stay black on the headset or an error to be reported that the pipewire device is missing or can even result in SteamVR crashing. ### Fix @@ -84,4 +84,4 @@ This might be caused by [[PERF] Subpar GPU performance due to wrong power profil ### Fix -Using CoreCtrl is highly advised (install it using your distribution package management) and in settings set your GPU to VR profile, as well as cpu to performance profile (if it's old Ryzen cpu). \ No newline at end of file +Using CoreCtrl is highly advised (install it using your distribution package management) and in settings set your GPU to VR profile, as well as cpu to performance profile (if it's old Ryzen cpu). diff --git a/wiki/Settings-tutorial.md b/wiki/Settings-tutorial.md index 9bdf82b3d7..617d95c5b0 100644 --- a/wiki/Settings-tutorial.md +++ b/wiki/Settings-tutorial.md @@ -7,7 +7,7 @@ as well as give you some pointers to troubleshoot common configuration issues. ## Prerequisites -* You have installed the ALVR streamer on your PC, and the ALVR client on your HMD. +* You have installed the ALVR streamer on your PC, and the ALVR app on your HMD. * You can launch up to the SteamVR void (or up to the SteamVR home) and are able to launch games. ## Step 1: choose resolution, refresh rate, codec @@ -42,7 +42,7 @@ Slowly increase bitrate until one of two things happen: ## Step 4: tweak frame buffering -If you notice micro-stuttering in the client, especially in busy scenes with fast motion, slowly increase maxBufferingFrames until the playback is smooth. +If you notice micro-stuttering on the headset, especially in busy scenes with fast motion, slowly increase maxBufferingFrames until the playback is smooth. Keep in mind that increasing maxBufferingFrames will linearly increase latency; if the value that gives a smooth playback results in too high of a latency for diff --git a/wiki/Troubleshooting.md b/wiki/Troubleshooting.md index 62c6b5410f..b7cbc561eb 100644 --- a/wiki/Troubleshooting.md +++ b/wiki/Troubleshooting.md @@ -78,14 +78,14 @@ ALVR on the headset stuck on `Searching for streamer...` This issue can have multiple causes. It is likely that the issue is with the PC ALVR application. See below for more specific issues. -ALVR client list is empty +ALVR device list is empty --- -![Empty ALVR client list](images/ALVRexe-no-clients.png) +![Empty ALVR device list](images/ALVRexe-no-clients.png) Check that the PC app and the headset app run on the latest version of ALVR. If your version is v2.3.1 or v2.4.0-alpha5 then you downloaded ALVR from the wrong link. The correct link is . -Make sure ALVR is running both on the PC and on the headset. To be visible in the client list, ALVR on the headset sends broadcast packets which the PC application listens for. These can be blocked by your firewall or possibly your router, if both headset and PC are connected wirelessly, having AP isolation enabled on the router will cause this. +Make sure ALVR is running both on the PC and on the headset. To be visible in the device list, ALVR on the headset sends broadcast packets which the PC application listens for. These can be blocked by your firewall or possibly your router, if both headset and PC are connected wirelessly, having AP isolation enabled on the router will cause this. To fix this, you can try the following: @@ -94,7 +94,7 @@ To fix this, you can try the following: * Open ports 9943 and 9944 on your firewall * Disable the PMF (Protected Management Frames) setting on your Router -If pinging works but you still don't see the client on the streamer app, then headset and PC might be on separate subnets. To solve this you can add the client manually. +If pinging works but you still don't see the device on the streamer app, then headset and PC might be on separate subnets. To solve this you can add the device manually. In the Devices tab press `Add device manually`. Fill in the fields with a name for your headset (you can use the name you want), the hostname (you can read it in the welcome screen in your headset when you open the ALVR app), the IP of the headset and then press `Save`. SteamVR says "headset not detected" @@ -127,7 +127,7 @@ ALVR sees the headset, SteamVR shows headset icon ![SteamVR waiting...](images/SteamVR-waiting.png) -This is a situation where you have ALVR open on both headset and PC, you can see the headset in the client list and trust it. ALVR then starts SteamVR automatically when you try connecting and SteamVR shows an icon for the headset (and controllers). +This is a situation where you have ALVR open on both headset and PC, you can see the headset in the device list and trust it. ALVR then starts SteamVR automatically when you try connecting and SteamVR shows an icon for the headset (and controllers). First make sure that SteamVR (more specifically, vrserver.exe) is allowed incoming connections (UDP, port 9944) in your firewall. You can also try disabling your firewall for testing, but you keep it disabled to use ALVR. @@ -139,7 +139,7 @@ You can try restarting ALVR on both the headset and the PC. On the headset, when ![latency graph of overloaded encoder](images/latency-graphs/overloaded-encoder.png) -Symptoms: stuttery playback in the client, streamer FPS is stable but below the target refresh rate. +Symptoms: stuttery playback on the headset, streamer FPS is stable but below the target refresh rate. Solution: increase foveation settings or decrease refresh rate. @@ -163,7 +163,7 @@ Solution: check that HMD is using 5G frequency and that no other device is conne ![latency graph of overloaded streamer](images/latency-graphs/overloaded-streamer.png) -Symptoms: stuttery playback in the client, streamer FPS dips or fluctuates below the target refresh rate. +Symptoms: stuttery playback on the headset, streamer FPS dips or fluctuates below the target refresh rate. Solution: @@ -174,7 +174,7 @@ Solution: ### Micro-stuttering -![latency graph of client stuttering](images/latency-graphs/not-enough-buffering.png) +![latency graph of headset stuttering](images/latency-graphs/not-enough-buffering.png) Symptoms: image is not always smooth especially in high motion or fast scenes. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md index ad1e563c17..35ecdcd700 100644 --- a/wiki/_Sidebar.md +++ b/wiki/_Sidebar.md @@ -14,7 +14,7 @@ * [Information and Recommendations](https://github.com/alvr-org/ALVR/wiki/Information-and-Recommendations) -* [ALVR client and streamer on separate networks](https://github.com/alvr-org/ALVR/wiki/ALVR-client-and-streamer-on-separate-networks) +* [ALVR headset and streamer on separate networks](https://github.com/alvr-org/ALVR/wiki/ALVR-client-and-streamer-on-separate-networks) * [Fixed Foveated Rendering (FFR)](https://github.com/alvr-org/ALVR/wiki/Fixed-Foveated-Rendering-(FFR)) From bb185672b71f318a0fbb18d9bca3ba281ac3b74f Mon Sep 17 00:00:00 2001 From: Leonhard Saam Date: Wed, 10 Jul 2024 09:09:26 +0200 Subject: [PATCH 6/8] docs: update no device image --- wiki/Troubleshooting.md | 2 +- wiki/images/ALVRexe-no-clients.png | Bin 35808 -> 0 bytes wiki/images/ALVRexe-no-devices.png | Bin 0 -> 29834 bytes 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 wiki/images/ALVRexe-no-clients.png create mode 100644 wiki/images/ALVRexe-no-devices.png diff --git a/wiki/Troubleshooting.md b/wiki/Troubleshooting.md index b7cbc561eb..b74c79bfc5 100644 --- a/wiki/Troubleshooting.md +++ b/wiki/Troubleshooting.md @@ -81,7 +81,7 @@ This issue can have multiple causes. It is likely that the issue is with the PC ALVR device list is empty --- -![Empty ALVR device list](images/ALVRexe-no-clients.png) +![Empty ALVR device list](images/ALVRexe-no-devices.png) Check that the PC app and the headset app run on the latest version of ALVR. If your version is v2.3.1 or v2.4.0-alpha5 then you downloaded ALVR from the wrong link. The correct link is . diff --git a/wiki/images/ALVRexe-no-clients.png b/wiki/images/ALVRexe-no-clients.png deleted file mode 100644 index 6daef18f03eb9443dcf29896960a89714cbf3942..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35808 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 diff --git a/wiki/images/ALVRexe-no-devices.png b/wiki/images/ALVRexe-no-devices.png new file mode 100644 index 0000000000000000000000000000000000000000..8d4292877c53c3ed3b93c8a67383087c460e57b9 GIT binary patch literal 29834 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 literal 0 HcmV?d00001 From fe0fa5c8de6aaa1ef6970f0fe4efe0eaf837052e Mon Sep 17 00:00:00 2001 From: Leonhard Saam Date: Wed, 10 Jul 2024 17:40:21 +0200 Subject: [PATCH 7/8] docs: don't update dev stuff --- wiki/How-ALVR-works.md | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/wiki/How-ALVR-works.md b/wiki/How-ALVR-works.md index 6c9b89996c..6ad5cec08f 100644 --- a/wiki/How-ALVR-works.md +++ b/wiki/How-ALVR-works.md @@ -21,11 +21,11 @@ This document was last updated on June 27th 2023 and refers to the master branch * Driver communication * Driver lifecycle * The streaming pipeline: Overview -* Device-driver communication +* Client-driver communication * Discovery * Streaming * SteamVR driver -* Device and driver compositors +* Client and driver compositors * Foveated rendering * Color correction * Video transcoding @@ -40,29 +40,29 @@ This document was last updated on June 27th 2023 and refers to the master branch ### The packaged application -ALVR is made of two applications: the streamer and device. The streamer can be installed on Windows and Linux, while the device app is installed on Android VR headsets. The device app communicates with the driver through TCP or UDP sockets. +ALVR is made of two applications: the streamer and client. The streamer can be installed on Windows and Linux, while the client is installed on Android VR headsets. The client communicates with the driver through TCP or UDP sockets. -The app is a single unified APK, named `alvr_client_android.apk`. It is powered by OpenXR and it is compatible with Quest headsets, recent Pico headsets and HTC Focus 3 and XR Elite. +The client is a single unified APK, named `alvr_client_android.apk`. It is powered by OpenXR and it is compatible with Quest headsets, recent Pico headsets and HTC Focus 3 and XR Elite. The streamer is made of two main parts: the dashboard and the driver (also known as server). The driver is dynamically loaded by SteamVR. This is the file structure on Windows: * `bin/win64/` - * `driver_alvr_server.dll`: The main binary, responsible for device discovery and streaming. Loaded by SteamVR. + * `driver_alvr_server.dll`: The main binary, responsible for client discovery and streaming. Loaded by SteamVR. * `driver_alvr_server.pdb`: Debugging symbols * `openvr_api.dll`: OpenVR SDK used for updating the chaperone. * `vcruntime140_1.dll`: Windows SDK used by C++ code in the driver. -* `ALVR Dashboad.exe`: Dashboard binary used to change settings, manage devices, monitor statistics and do installation actions. It can launch SteamVR. +* `ALVR Dashboad.exe`: Dashboard binary used to change settings, manage clients, monitor statistics and do installation actions. It can launch SteamVR. * `driver.vrdrivermanifest`: Auxiliary config file used by the driver. At runtime, some other files are created: -* `session.json`: This contains unified configuration data used by ALVR, such as settings and device records. -* `session_log.txt`: Main log file. Each line is a json structure and represents an event generated by the driver. This gets cleared each time a device connects. +* `session.json`: This contains unified configuration data used by ALVR, such as settings and client records. +* `session_log.txt`: Main log file. Each line is a json structure and represents an event generated by the driver. This gets cleared each time a client connects. * `crash_log.txt`: Auxiliary log file. Same as `session_log.txt`, except only error logs are saved, and does not get cleared. ### Programming languages -ALVR is written in multiple languages: Rust, C, C++, HLSL, GLSL. The main language used in the codebase is Rust, which is used for the dashboard, networking, video decoding and audio code. C and C++ are used for graphics, video encoding and SteamVR integration. HLSL is used for graphics shaders on the Windows driver, GLSL is used on the Linux driver and the app. Moving forward, more code will be rewritten from C/C++ to Rust and HLSL code will be moved to GLSL or WGSL. +ALVR is written in multiple languages: Rust, C, C++, HLSL, GLSL. The main language used in the codebase is Rust, which is used for the dashboard, networking, video decoding and audio code. C and C++ are used for graphics, video encoding and SteamVR integration. HLSL is used for graphics shaders on the Windows driver, GLSL is used on the Linux driver and the client. Moving forward, more code will be rewritten from C/C++ to Rust and HLSL code will be moved to GLSL or WGSL. Rust is a system programming language focused on memory safety and ease of use. It is as performant as C++ but Rust code is less likely to be affected by runtime bugs. The prime feature Rust feature used by ALVR is enums, that correspond to tagged unions in C++. Rust's enum is a data type that stores different kinds of data, but only one type can be accessed at a time. For example the type `Result` can contain either an `Ok` value or an `Err` value but not both. Together with pattern matching, this is the foundation of error management in Rust applications. @@ -72,19 +72,19 @@ ALVR code is hosted in a monorepo. This is an overview of the git tree: * `.github/`: Contains scripts used by the GitHub CI. * `alvr/`: Each subfolder is a Rust crate ("crate" means a code library or executable). - * `audio/`: Utility crate hosting audio related code shared by app and driver. - * `client_core/`: Platform agnostic code for the app. It is used as a Rust library for `alvr_client_openxr` and can also compiled to a C ABI shared library with a .h header for integration with other projects. - * `client_mock/`: Device mock implemented as a thin wrapper around `alvr_client_core`. - * `client_openxr/`: App implementation using OpenXR, compiled to a APK binary. + * `audio/`: Utility crate hosting audio related code shared by client and driver. + * `client_core/`: Platform agnostic code for the client. It is used as a Rust library for `alvr_client_openxr` and can also compiled to a C ABI shared library with a .h header for integration with other projects. + * `client_mock/`: Client mock implemented as a thin wrapper around `alvr_client_core`. + * `client_openxr/`: Client implementation using OpenXR, compiled to a APK binary. * `common/`: Some common code shared by other crates. It contains code for versioning, logging, struct primitives, and OpenXR paths. * `dashboard/`: The dashboard application. * `events/`: Utility crate hosting code related to events. * `filesystem/`: Utility crate hosting code for filesystem abstraction between Windows and Linux. - * `packets/`: Utility crate containing packet definitions for communication between device, driver and dashboard. + * `packets/`: Utility crate containing packet definitions for communication between client, driver and dashboard. * `server/`: The driver shared library loaded by SteamVR. * `server_io/`: Common functionality shared by dashboard and driver, for interaction with the host system. This allows dashboard and driver to work independently from each other. * `session/`: Utility crate related to session file and data management. - * `sockets/`: Utility crate shared by app and driver with socket and protocol implementation. + * `sockets/`: Utility crate shared by client and driver with socket and protocol implementation. * `vrcompositor_wrapper/`: Small script used on Linux to correctly load the ALVR Vulkan layer by SteamVR. * `vulkan_layer/`: Vulkan WSI layer used on Linux to work around limitations of the OpenVR API on Linux. This is mostly patchwork and hopefully will be removed in the future. * `xtask/`: Utility CLI hosting a variety of scripts for environment setting, building, and packaging ALVR. Should be called with `cargo xtask`. @@ -144,7 +144,7 @@ Log is a special kind of event: The driver logs events in JSON form to `session.json`, one per line. -Currently its use is limited, but eventually this will replace the current logging system, and logging will be built on top of the event system. The goal is to create a unified star-shaped network where each device and dashboard instance sends events to the server and the server broadcasts events to all other devices and dashboard instances. This should also unify the way the server communicates with devices and dashboards, making the dashboard just another client. +Currently its use is limited, but eventually this will replace the current logging system, and logging will be built on top of the event system. The goal is to create a unified star-shaped network where each client and dashboard instance sends events to the server and the server broadcasts events to all other clients and dashboard instances. This should also unify the way the server communicates with clients and dashboards, making the dashboard just another client. ## Session and settings @@ -153,7 +153,7 @@ ALVR uses a unified configuration file, that is `session.json`. It is generated * `"server_version"`: the current version of the streamer. It helps during a version upgrade. * `"drivers_backup"`: temporary storage for SteamVR driver paths. Used by the dashboard. * `"openvr_config"`: contains a list of settings that have been checked for a diff. It is used by C++ code inside the driver. -* `"client_connections"`: contains entries corresponding to known devices. +* `"client_connections"`: contains entries corresponding to known clients. * `"session_settings"`: all ALVR settings, laid in a tree structure. ### Procedural generation of code and UI @@ -183,7 +183,7 @@ These are the main components: TODO: Add screenshots * Sidebar: is used to select the tab for the main content page. -* Devices tab: used to trust devices or add them manually specifying the IP +* Devices tab: used to trust clients or add them manually specifying the IP * Statistics tab: shows graphs for latency and FPS and a summary page * Settings tab: settings page split between `Presets` and `All Settings`. `All Settings` are procedurally generated from a schema. `Presets` are controls that modify other settings. * Installation tab: utilities for installation: setting firewall rules, registering the driver, launching the setup wizard. @@ -201,7 +201,7 @@ The dashboard communicates with the driver in order to update its information an * `/api/events`: This endpoint is upgraded to a websocket and is used for listening to events from the driver * `/api/ping`: returns code 200 when the driver is alive. -The dashboard retains some functionality when the driver is not launched. It can manage settings, devices and perform installation actions, but devices cannot be discovered. Once The driver is launched all these actions are performed by the server, requested with the HTTP API. This mechanism ensures that there are no data races. +The dashboard retains some functionality when the driver is not launched. It can manage settings, clients and perform installation actions, but clients cannot be discovered. Once The driver is launched all these actions are performed by the server, requested with the HTTP API. This mechanism ensures that there are no data races. ### Driver lifecycle @@ -229,26 +229,26 @@ This might seem unnecessarily complicated. The reason for the first message roun The goal of ALVR is to bridge input and output of a PCVR application to a remote headset. In order to do this ALVR implements pipelines to handle input, video and audio. The tracking-video pipeline (as known as the motion-to-photon pipeline) is the most complex one and it can be summarized in the following steps: -* Poll tracking data on the device +* Poll tracking data on the client * Send tracking to the driver * Execute the PCVR game logic and render layers * Compose layers into a frame * Encode the video frame -* Send the encoded video frame to the device through the network -* Decode the video frame on the device +* Send the encoded video frame to the client through the network +* Decode the video frame on the client * Perform more compositor transformations * Submit the frame to the VR runtime * The runtime renders the frame during a vsync. -## Device-driver communication +## Client-driver communication -ALVR uses a custom protocol for device-driver communication. ALVR supports UDP and TCP transports. USB connection is supported although not as a first class feature; you can read more about it [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)). +ALVR uses a custom protocol for client-driver communication. ALVR supports UDP and TCP transports. USB connection is supported although not as a first class feature; you can read more about it [here](https://github.com/alvr-org/ALVR/wiki/ALVR-wired-setup-(ALVR-over-USB)). ### Discovery -Usually the first step to establish a connection is discovery. When the server discovers a device it shows it in the "New devices" section in the Devices tab. The user can then trust the device and the connection is established. +Usually the first step to establish a connection is discovery. When the server discovers a client it shows it in the "New devices" section in the Devices tab. The user can then trust the client and the connection is established. -ALVR uses a UDP socket at 9943 for discovery. The device app broadcasts a packet and waits for the driver to respond. It's the device that broadcasts and it's the driver that then asks for a connection: this is because of the balance in responsibility of the two peers. The device becomes the portal to a PC, that can contain sensitive data. For this reason the server has to trust the device before initiating the connection. +ALVR uses a UDP socket at 9943 for discovery. The client broadcasts a packet and waits for the driver to respond. It's the client that broadcasts and it's the driver that then asks for a connection: this is because of the balance in responsibility of the two peers. The client becomes the portal though a PC, that can contain sensitive data. For this reason the server has to trust the client before initiating the connection. This is the layout of the discovery packet @@ -256,9 +256,9 @@ This is the layout of the discovery packet | :---------------: | :---------: | :------: | | "ALVR" + 0x0 x 12 | 8 bytes | 32 bytes | -* The prefix is used to filter packets and ensure a packet is really sent by an ALVR device -* The protocol ID is a unique version identifier calculated from the semver version of the app. If the app version is *semver-compatible* with the streamer, the protocol ID will match. -* Hostname: the hostname is a unique identifier for a device. When a device is launched for the first time, an hostname is chosen and it persists for then successive launches. It is reset when the app is upgraded or downgraded. +* The prefix is used to filter packets and ensure a packet is really sent by an ALVR client +* The protocol ID is a unique version identifier calculated from the semver version of the client. If the client version is *semver-compatible* with the streamer, the protocol ID will match. +* Hostname: the hostname is a unique identifier for a client. When a client is launched for the first time, an hostname is chosen and it persists for then successive launches. It is reset when the app is upgraded or downgraded. The format of the packet can change between major versions, but the prefix must remain unchanged, and the protocol ID must be 8 bytes. @@ -266,13 +266,13 @@ The format of the packet can change between major versions, but the prefix must ALVR uses two sockets for streaming: the control socket and stream socket. Currently these are implemented with async code; there's a plan to move this back to sync code. -The control socket uses the TCP transport; it is used to exchange small messages between device and server, ALVR requires TCP to ensure reliability. +The control socket uses the TCP transport; it is used to exchange small messages between client and server, ALVR requires TCP to ensure reliability. The stream socket can use UDP or TCP; it is used to send large packets and/or packets that do not require reliability, ALVR is robust to packet losses and packet reordering. The specific packet format used over the network is not clearly defined since ALVR uses multiple abstraction layers to manipulate the data (bincode, tokio Length Delimited Coding). Furthermore, packets are broken up into shards to ensure they can support the MTU when using UDP. -Since the amount of data streamed is large, the socket buffer size is increased both on the driver side and on the device. +Since the amount of data streamed is large, the socket buffer size is increased both on the driver side and on the client. ## SteamVR driver @@ -280,17 +280,17 @@ The driver is the component responsible for most of the streamer functionality. Using the OpenVR API, ALVR pushes tracking and button data to SteamVR using `vr::VRServerDriverHost()->TrackedDevicePoseUpdated()`. SteamVR then returns a rendered game frame with associated pose used for rendering. On Windows, frames are retrieved implementing the `IVRDriverDirectModeComponent` interface: SteamVR calls `IVRDriverDirectModeComponent::Present()`. On Linux this API doesn't work, and so ALVR uses a WSI Vulkan layer to intercept display driver calls done by vrcompositor. The pose associated to the frame is obtained from the vrcompositor execution stack with the help of libunwind. -## Device and driver compositors +## Client and driver compositors ALVR is essentially a bridge between PC and headset that transmits tracking, audio and video. But it also implements some additional features to improve image quality and streaming performance. To this goal, ALVR implements Fixed Foveated Rendering (FFR) and color correction. -The app compositor is implemented in OpenGL, while on the server it's either implemented with DirectX 11 on Windows or Vulkan on Linux. There are plans to move all compositor code to the graphics abstraction layer [wgpu](https://github.com/gfx-rs/wgpu), mainly for unifying the codebase. +The client compositor is implemented in OpenGL, while on the server it's either implemented with DirectX 11 on Windows or Vulkan on Linux. There are plans to move all compositor code to the graphics abstraction layer [wgpu](https://github.com/gfx-rs/wgpu), mainly for unifying the codebase. It's important to note that ALVR's compositors are separate from the headset runtime compositor and SteamVR compositors. The headset runtime compositor is part of the headset operative system and controls compositing between different apps and overlays, and prepares the image for display (with lens distortion correction, chroma aberration correction, mura and ghosting correction). On the driver side, on Windows ALVR takes responsibility for compositing layers returned by SteamVR. The only responsibility of SteamVR is converting the frame into a valid DXGI texture if the game uses OpenGL or Vulkan graphics. On Linux ALVR grabs Vulkan frames that are already composited by vrcompostor. This introduced additional challenges since vrcompositor implements async reprojection which disrupts our head tracking mechanism. ### Foveated encoding -Foveated rendering is a technique where frame images are individually compressed in a way that the human eye barely detects the compression. Particularly, the center of the image is kept at original resolution, and the rest is compressed. ALVR refers to foveated rendering as "Foveated encoding" to clarify its scope. In native standalone or PCVR apps, foveated rendering reduces the load on the GPU by rendering parts of the image ar lower resolution. In ALVR's case frames are still rendered at full resolution, but are then "encoded" (compressing the outskirts of the image) before actually encoding and transmitting them. The image is then reexpanded on the device side after decoding and before display. +Foveated rendering is a technique where frame images are individually compressed in a way that the human eye barely detects the compression. Particularly, the center of the image is kept at original resolution, and the rest is compressed. ALVR refers to foveated rendering as "Foveated encoding" to clarify its scope. In native standalone or PCVR apps, foveated rendering reduces the load on the GPU by rendering parts of the image ar lower resolution. In ALVR's case frames are still rendered at full resolution, but are then "encoded" (compressing the outskirts of the image) before actually encoding and transmitting them. The image is then reexpanded on the client side after decoding and before display. Currently ALVR supports only fixed foveation, but support for tracked eye foveation is planned. @@ -302,15 +302,15 @@ Color correction is implemented on the server and adds simple brightness, contra ## Video transcoding -To be able to send frames from driver to the device through the network, they have to be compressed since current WiFi technology doesn't allow to send the amount of data of raw frames. Doing a quick conservative calculation, let's say we have 2 x 2048x2048 eye images, 3 color channels, 8 bits per channel, sent 72 times per second, that would amount to almost 15 Gbps. +To be able to send frames from driver to client through the network, they have to be compressed since current WiFi technology doesn't allow to send the amount of data of raw frames. Doing a quick conservative calculation, let's say we have 2 x 2048x2048 eye images, 3 color channels, 8 bits per channel, sent 72 times per second, that would amount to almost 15 Gbps. -ALVR uses h264 and HEVC video codecs for compression. These codecs are chosen since they have hardware decoding support on Android and generally hardware encoding support on the PC side. On Windows, the driver uses NvEnc for Nvidia GPUs and AMF for AMD GPUS; on Linux ALVR supports VAAPI, NvEnc and AMF through FFmpeg. In case the GPU doesn't support hardware encoding, on both Windows and Linux ALVR supports software encoding with x264 (through FFmpeg), although the performance is often insufficient for a smooth experience. The app supports only MediaCodec, which is the API to access hardware video codecs on Android. +ALVR uses h264 and HEVC video codecs for compression. These codecs are chosen since they have hardware decoding support on Android and generally hardware encoding support on the PC side. On Windows, the driver uses NvEnc for Nvidia GPUs and AMF for AMD GPUS; on Linux ALVR supports VAAPI, NvEnc and AMF through FFmpeg. In case the GPU doesn't support hardware encoding, on both Windows and Linux ALVR supports software encoding with x264 (through FFmpeg), although the performance is often insufficient for a smooth experience. The client supports only MediaCodec, which is the API to access hardware video codecs on Android. h264 and HEVC codecs compression works on the assumption that consecutive frames are similar to each other. Each frame is reconstructed from past frames + some small additional data. For this reason, packet losses may cause glitches that persist many frames after the missing frame. When ALVR detects packet losses, it requests a new IDR frame from the encoder. A IDR frame is a packet that contains all the information to build a whole frame by itself; the encoder will ensure that successive frames will not rely on older frames than the last requested IDR. ## Audio -Game audio is captured on the PC and sent to the device, and microphone audio is captured on the device and sent to the PC. Windows and Linux implementation once again differ. On Windows, game audio is captured from a loopback device; microphone is is sent to virtual audio cable software to expose audio data from a (virtual) input device. On Linux the microphone does not work out-of-the-box, but there is a bash script available for creating and plugging into pipewire audio devices. +Game audio is captured on the PC and sent to the client, and microphone audio is captured on the client and sent to the PC. Windows and Linux implementation once again differ. On Windows, game audio is captured from a loopback device; microphone is is sent to virtual audio cable software to expose audio data from a (virtual) input device. On Linux the microphone does not work out-of-the-box, but there is a bash script available for creating and plugging into pipewire audio devices. Unlike for video, audio is sent as a raw PCM waveform and new packets do not rely on old packets. But packet losses may still cause popping, which happens when there is a sudden jump in the waveform. To mitigate this, when ALVR detects a packet loss (or a buffer overflow or underflow) it will render a fadeout or cross-fade. @@ -326,7 +326,7 @@ On the streamer side, ALVR needs to workaround a OpenVR API limitation. SteamVR ## Other streams -There are some other kinds of data which can be streamed without requiring any special timing. These are button presses and haptics, respectively sent from the device to the driver and from the driver to the device. +There are some other kinds of data which can be streamed without requiring any special timing. These are button presses and haptics, respectively sent from client to driver and from driver to client. ## Upcoming @@ -336,7 +336,7 @@ Phase sync is not a single algorithm but many that share similar objectives, red In general, a phase sync algorithm is composed of two parts: a queue that holds data resources or pointers, and a statistical model to predict event times. The statistical model is fed with duration or other kinds of timing samples and as output it returns a refined time prediction for a recurring event. The statistical model could be simple and just aim for a average-controlled event, or more complex that aims for submitting for a deadline; the second case needs to take into account the variance of the timing samples. Unlike Oculus implementation, these statistical models can be highly configurable to tune the target mean or target variance. -There are a few phase sync algorithms planned to be implemented: frame submission timing (to reduce frame queueing on the headset, controlled by shifting the phase of the driver rendering cycle), SteamVR tracking submission timing (to make sure SteamVR is using exactly the tracking sample we want) and tracking poll timing (to reduce queuing on the server side). +There are a few phase sync algorithms planned to be implemented: frame submission timing (to reduce frame queueing on the client, controlled by shifting the phase of the driver rendering cycle), SteamVR tracking submission timing (to make sure SteamVR is using exactly the tracking sample we want) and tracking poll timing (to reduce queuing on the server side). ## Sliced encoding From 7f5ec4a8a5c44608629b80e74af9ffab88d7f076 Mon Sep 17 00:00:00 2001 From: plyshka Date: Wed, 10 Jul 2024 21:07:27 +0500 Subject: [PATCH 8/8] docs: remove oudated sidebar link --- wiki/_Sidebar.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md index 35ecdcd700..4a7ab2c260 100644 --- a/wiki/_Sidebar.md +++ b/wiki/_Sidebar.md @@ -46,6 +46,4 @@ * [How ALVR works](https://github.com/alvr-org/ALVR/wiki/How-ALVR-works) -* [Linux support](https://github.com/alvr-org/ALVR/wiki/Linux-Support-development-progress) - * [Real time video upscaling experiments](https://github.com/alvr-org/ALVR/wiki/Real-time-video-upscaling-experiments)