Nvidia: automatic detection of the version

The automatic detection uses the /proc interface proposed by the Nvidia
kernel module.

Having autodetection inside nix, I also changed the way the driver is
fetched, so everything is done inside nix. So no more `nvidiaInstall`
wrapper script.

I updated the `README` accordingly.
This commit is contained in:
Guillaume Bouchard 2020-04-26 18:22:15 +02:00
parent 1ba64e0199
commit d5c0d28968
3 changed files with 132 additions and 146 deletions

214
README.md
View File

@ -1,29 +1,15 @@
This tool tries to solve the "OpenGL" problem on nix. Works with Nvidia cards (with bumblebee) and most of the card supported by mesa (such as Intel, AMD and nouveau using the free driver). It works for Vulkan programs too. # NixGL
# Quick start NixGL solve the "OpenGL" problem with [nix](https://nixos.org/nix/). Works all mesa
drivers (intel cards and "free" version fro Nvidia or AMD cards), Nvidia
Suppose you have a configuration with an nvidia card, host driver version `390.25` proprietary drivers, even with hybrid configuration (with bumblebee). It works
for Vulkan programs too.
```
# clone the repository
git clone https://github.com/guibou/nixGL
cd nixGL
# build and install the wrapper
./nvidiaInstall.py 390.25 nixGLNvidia
# install the wrapper
nix-env -i ./result
# use it with any OpenGL application
nixGLNvidia blender
```
# Motivation # Motivation
You use Nix on any distribution, and any GL application installed fails with this error: You use Nix on any distribution, and any GL application installed fails with this error:
``` ```bash
$ program $ program
libGL error: unable to load driver: i965_dri.so libGL error: unable to load driver: i965_dri.so
libGL error: driver pointer missing libGL error: driver pointer missing
@ -35,112 +21,80 @@ libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast libGL error: failed to load driver: swrast
``` ```
This library contains a wrapper which is able to launch GL or Vulkan applications: NixGL provides a set of wrappers able to launch GL or Vulkan applications:
``` ```bash
nixGLXXX program $ nixGLXXX program
nixVulkanXXX program $ nixVulkanXXX program
``` ```
# Installation / Usage # Installation
Clone this git repository: Clone this git repository:
``` ```bash
git clone https://github.com/guibou/nixGL $ git clone https://github.com/guibou/nixGL
cd nixGL $ cd nixGL
``` ```
## Optional (if NVIDIA): Grab your NVIDIA driver version Many wrappers are available, depending on your hardware and the graphical API
you want to use (i.e. Vulkan or OpenGL). You may want to install a few of them,
for example if you want to support OpenGL and Vulkan on a laptop with an hybrid
configuration.
Using `glxinfo` from your host system, grab the driver version, here `390.25`: ## OpenGL wrappers
``` - `nix-env -f ./ -iA nixGLIntel`: Mesa OpenGL implementation (intel, amd, nouveau, ...).
$ glxinfo | grep NVIDIA - `nix-env -f ./ -iA nixGLNvidiaBumblebee`: Proprietary Nvidia driver on hybrid hardware.
... - `nix-env -f ./ -iA nixGLNvidia`: Proprietary Nvidia driver.
OpenGL core profile version string: 4.5.0 NVIDIA 390.25
...
```
## Build ## Vulkan wrappers
### For mesa (intel, amd, nouveau, ...) - `nix-env -f ./ -iA nixVulkanNvidia`: Proprietary Nvidia driver.
- `nix-env -f ./ -iA nixVulkanIntel`: Mesa Vulkan implementation.
For mesa (intel, amd, nouveau) GL, the package is historically called `nixGLIntel`:
```
nix-build -A nixGLIntel
```
For Intel Vulkan:
```
nix-build -A nixVulkanIntel
```
### For nvidia
Due to some restriction on `nix` 2.0, the `nix-build` must be called with a wrapper script.
For NVIDIA GL alone:
```
./nvidiaInstall.py 390.25 nixGLNvidia
```
For NVIDIA Vulkan alone:
The Vulkan wrapper also sets `VK_LAYER_PATH` the the validation layers in the nix store.
```
./nvidiaInstall.py 390.25 nixVulkanNvidia
```
(replace `390.25` with the host driver version gathered earlier.)
For Nvidia with bumblebee:
```
./nvidiaInstall.py 390.25 nixGLNvidiaBumblebee
```
(replace `390.25` with the host driver version gathered earlier.)
## Install
The previous commands only build the wrapper, now stored inside `./result`, you need to install it:
```
nix-env -i ./result
```
(Note, you can iterate many time on this process to install as many driver as needed. Common example are `nixGLIntel` with `nixGLNvidiaBumblebee`)
The Vulkan wrapper also sets `VK_LAYER_PATH` the validation layers in the nix store.
# Usage # Usage
For GL programs Just launch the program you want prefixed by the right wrapped.
``` For OpenGL programs:
nixGLXXX program args
```
For Vulkan programs
```
nixVulkanXXX program args
```
For example (on my dual GPU laptop):
```bash ```bash
$ nixGLIntel glxinfo | grep -i 'OpenGL version string' $ nixGLXXX program args
```
For Vulkan programs:
```bash
$ nixVulkanXXX program args
```
Replace `XXX` by the implementation pour previously selected, such as `nixGLIntel` or `nixGLNvidia`.
## Examples
# OpenGL - Hybrid Intel + Nvidia laptop
After installing `nixGLIntel` and `nixGLNvidiaBumblebee`.
```bash
$ nixGLIntel $(nix run nixpkgs.glxinfo -c glxinfo) | grep -i 'OpenGL version string'
OpenGL version string: 3.0 Mesa 17.3.3 OpenGL version string: 3.0 Mesa 17.3.3
$ nixGLNvidiaBumblebee glxinfo | grep -i 'OpenGL version string' $ nixGLNvidiaBumblebee $(nix run nixpkgs.glxinfo -c glxinfo) | grep -i 'OpenGL version string'
OpenGL version string: 4.6.0 NVIDIA 390.25 OpenGL version string: 4.6.0 NVIDIA 390.25
``` ```
Another example (on an XPS 9560 with the Intel GPU selected): If the program you'd like to run is already installed by nix in your current environment, you can simply run it with the wrapper, for example:
```bash
$ nixGLIntel blender
```
# Vulkan - Intel GPU
After installing `nixVulkanIntel`.
```bash ```bash
$ sudo apt install mesa-vulkan-drivers $ sudo apt install mesa-vulkan-drivers
@ -156,34 +110,38 @@ VkPhysicalDeviceProperties:
deviceName = Intel(R) HD Graphics 630 (Kaby Lake GT2) deviceName = Intel(R) HD Graphics 630 (Kaby Lake GT2)
``` ```
# Limitations
Does not work now for AMD drivers because I dont' have the hardware.
# Comparaison with similar tools
[nix-install-vendor-gl.sh](https://github.com/deepfire/nix-install-vendor-gl)
provides a similar system with a different approach:
- it auto detect the host driver
- it needs root access and set your system for a specific driver
- it only provides wrappers for nvidia (without bumblebee)
Both projects are now really similar and the only reason I did not
contributed to `nix-install-vendor-gl.sh` was because initial `nixGL`
had a totally different approach.
# Troubleshooting # Troubleshooting
If by any chance it does not work, you need to install nixGL using the same nixpkgs checkout than the one of your application. For example: ## Nvidia auto detection does not work
```bash ```bash
NIX_PATH=nixpkgs=https://github.com/nixos/nixpkgs/archive/94d80eb72474bf8243b841058ce45eac2b163943.tar.gz nix build -f ./default.nix nixGLIntel building '/nix/store/ijs5h6h07faai0k74diiy5b2xlxh891g-auto-detect-nvidia.drv'...
pcregrep: Failed to open /proc/driver/nvidia/ersion: No such file or directory
builder for '/nix/store/ijs5h6h07faai0k74diiy5b2xlxh891g-auto-detect-nvidia.drv' failed with exit code 2
error: build of '/nix/store/ijs5h6h07faai0k74diiy5b2xlxh891g-auto-detect-nvidia.drv' faile
``` ```
# Old nvidia drivers You can run the Nvidia installer using an explicit version string instead of the automatic detection method:
Users of nvidia legacy driver should use the `backport/noGLVND` branch. ```bash
nix-build -A nixGLNvidia --argstr nvidiaVersion 440.82
```
The version of your driver can be found using `glxinfo` from your system default package manager, or `nvidia-settings`.
## On nixOS
`nixGL` can also be used on nixOS if the system is installed with a different
nixpkgs clone than the one your application are installed with. Override the
`pkgs` argument of the script with the correct nixpkgs clone:
```bash
nix-build ./default.nix -A nixGLIntel --arg pkgs "import path_to_your_nixpkgs {}".
```
## Old nvidia drivers
Users of Nvidia legacy driver should use the `backport/noGLVND` branch. This branch is not tested and may not work well, please open a bug report, it will be taken care of as soon as possible.
# `nixGLCommon` # `nixGLCommon`
@ -193,3 +151,11 @@ For example:
``` ```
nix-build -E "with import ./default.nix {}; nixGLCommon nixGLIntel" nix-build -E "with import ./default.nix {}; nixGLCommon nixGLIntel"
```
# Limitations
`nixGL` is badly tested, mostly because it is difficult to test automatically in a continuous integration context because you need access to different type of hardware.
Some OpenGL configurations may not work, for example AMD proprietary drivers. There is no fundamental limitation, so if you want support for theses configurations, open an issue.

View File

@ -1,5 +1,9 @@
{ system ? builtins.currentSystem, { system ? builtins.currentSystem,
## Nvidia informations.
# Version of the system kernel module. Let it to null to enable auto-detection.
nvidiaVersion ? null, nvidiaVersion ? null,
# Hash of the Nvidia driver .run file. null is fine, but fixing a value here
# will be more reproducible and more efficient.
nvidiaHash ? null, nvidiaHash ? null,
# Enable 32 bits driver # Enable 32 bits driver
# This is one by default, you can switch it to off if you want to reduce a # This is one by default, you can switch it to off if you want to reduce a
@ -9,17 +13,46 @@
}: }:
let let
# The nvidia version. Either fixed by the `nvidiaVersion` argument, or
# auto-detected.
_nvidiaVersion = if nvidiaVersion != null
then nvidiaVersion
else
# This is the auto-detection mecanism. This is ugly.
# We read /proc/driver/nvidia/version which is set by the Nvidia driver kernel module.
# This fails if the nvidia driver kernel module is not loaded.
# I'd like to just read the file using `${/proc/driver/nvidia/version}` and
# then let nix invalidate the derivation if the content of this file
# changes, but that's not possible, see
# https://github.com/NixOS/nix/issues/3539
# But /proc is readable at build time! So runCommand works fine.
import (nixpkgs.runCommand "auto-detect-nvidia" {
time = builtins.currentTime;
}
''
# Written this way so if the version file does not exists, the script crashs
VERSION="$(${nixpkgs.pcre}/bin/pcregrep -o1 'Module +([0-9]+\.[0-9]+)' /proc/driver/nvidia/version)"
echo "\"$VERSION\"" > $out
'');
addNvidiaVersion = drv: drv.overrideAttrs(oldAttrs: {
name = oldAttrs.name + "-${_nvidiaVersion}";
});
overlay = self: super: overlay = self: super:
{ {
linuxPackages = super.linuxPackages // linuxPackages = super.linuxPackages //
{ {
nvidia_x11 = (super.linuxPackages.nvidia_x11.override { nvidia_x11 = (super.linuxPackages.nvidia_x11.override {
}).overrideAttrs(oldAttrs: rec { }).overrideAttrs(oldAttrs: rec {
name = "nvidia-${nvidiaVersion}"; name = "nvidia-${_nvidiaVersion}";
src = super.fetchurl { src = let url ="http://download.nvidia.com/XFree86/Linux-x86_64/${_nvidiaVersion}/NVIDIA-Linux-x86_64-${_nvidiaVersion}.run";
url = "http://download.nvidia.com/XFree86/Linux-x86_64/${nvidiaVersion}/NVIDIA-Linux-x86_64-${nvidiaVersion}.run"; in if nvidiaHash != null
then super.fetchurl {
inherit url;
sha256 = nvidiaHash; sha256 = nvidiaHash;
}; } else
builtins.fetchurl url;
useGLVND = true; useGLVND = true;
}); });
}; };
@ -56,17 +89,17 @@ rec {
}; };
# TODO: 32bit version? Looks like it works fine without anything special. # TODO: 32bit version? Looks like it works fine without anything special.
nixGLNvidiaBumblebee = writeExecutable { nixGLNvidiaBumblebee = addNvidiaVersion (writeExecutable {
name = "nixGLNvidiaBumblebee"; name = "nixGLNvidiaBumblebee";
text = '' text = ''
#!/usr/bin/env sh #!/usr/bin/env sh
export LD_LIBRARY_PATH=${lib.makeLibraryPath [nvidia]}:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${lib.makeLibraryPath [nvidia]}:$LD_LIBRARY_PATH
${bumblebee}/bin/optirun --ldpath ${lib.makeLibraryPath [libglvnd nvidia]} "$@" ${bumblebee}/bin/optirun --ldpath ${lib.makeLibraryPath [libglvnd nvidia]} "$@"
''; '';
}; });
# TODO: 32bit version? Not tested. # TODO: 32bit version? Not tested.
nixNvidiaWrapper = api: writeExecutable { nixNvidiaWrapper = api: addNvidiaVersion (writeExecutable {
name = "nix${api}Nvidia"; name = "nix${api}Nvidia";
text = '' text = ''
#!/usr/bin/env sh #!/usr/bin/env sh
@ -79,7 +112,7 @@ rec {
}:''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} }:''${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
"$@" "$@"
''; '';
}; });
# TODO: 32bit version? Not tested. # TODO: 32bit version? Not tested.
nixGLNvidia = nixNvidiaWrapper "GL"; nixGLNvidia = nixNvidiaWrapper "GL";

View File

@ -1,13 +0,0 @@
#! /usr/bin/env nix-shell
#! nix-shell -i python3 -p python3
import sys
import subprocess
nvidiaVersion = sys.argv[1]
tool = sys.argv[2]
nvidiaUrl = f"http://download.nvidia.com/XFree86/Linux-x86_64/{nvidiaVersion}/NVIDIA-Linux-x86_64-{nvidiaVersion}.run"
nvidiaHash = subprocess.check_output(["nix-prefetch-url", nvidiaUrl]).strip()
subprocess.check_call(["nix-build", "-A", tool, "--argstr", "nvidiaVersion", nvidiaVersion, "--argstr", "nvidiaHash", nvidiaHash])