More robust solution for intel / nvidia / nvidia (bumblebee)

This commit is contained in:
Guillaume Bouchard 2018-02-17 22:49:34 +01:00
parent 73cd9002ad
commit 1046517dcd
3 changed files with 80 additions and 103 deletions

View File

@ -19,7 +19,7 @@ libGL error: failed to load driver: swrast
This library contains a wrapper which is able to launch GL application:
```
nixGL program
nixGLXXX program
```
# Installation / Usage
@ -30,53 +30,46 @@ Clone this git repository:
git clone https://github.com/guibou/nixGL
```
(Optional) installation:
Build / install
```
cd nixGL
nix-build
nix-build -A XXX
nix-env -i ./result
```
Usage:
XXX can be one of:
- `nixGLNvidia`: Nvidia driver without bumblebee (should work, but done from memory: please open a bug report if any issue)
- `nixGLNvidiaBumblebee`: Nvidia driver with bumblebee (tested)
- `nixGLIntel`: Intel driver (tested)
# Usage
```
nixGL program args
nixGLXXX program args
```
For example (on my dual GPU laptop):
```bash
$ nixGLIntel glxinfo | grep -i 'OpenGL version string'
OpenGL version string: 3.0 Mesa 17.3.3
$ nixGLNvidiaBumblebee glxinfo | grep -i 'OpenGL version string'
OpenGL version string: 4.6.0 NVIDIA 390.25
```
# Limitations
The idea is really simple and should work reliably in most cases.
The idea is really simple and should work reliably in most cases. It
can be easily extended to AMD drivers, I just don't have the hardware
to test. Contact me.
However there is still two configurations variables hardcoded in the wrapper.
*Important*: You need an host system driver which match the nixpkgs one. For example, at the time of this writing, nixpkgs contains nvidia `390.25`. Your host system must contain the same version. This limitation can be lifted by using a different version of nixpkgs:
- `ignored`: the list of all nix packages which may contain a wrong `libGL.so`.
- `systemLibs`: the list of where on the host system the `libGL.so` can be found.
Open a bug / pull request if this does not work on your distribution / driver.
It works with `primus`, but there is some artifacts, mostly due to the next fundamental issue:
## Fundamental issue
If your program libraries depends on different versions of the same library, for example, this dependency tree:
```
program
libFoo-1.2
libBar-1.4
libTurtle-1.6
libBar-1.2
```shell
export NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs-channels/archive/nixos-14.12.tar.gz
nix-build -A nixGLNvidia
```
One version or the other of `libBar` may be used. In practice this does not happen a lot.
A similar issue will happen if your system `libGL.so` depends on some library which are already in your program dependency list. Undefined behaviors can happen.
## Subprocessus
It does not work with subprocessus, that's all ;(
## Haskell Stack `exec`
You need to call `stack --nix exec -- nixGL yourProgram` instead of `nixGL stack exec -- yourProgram` du to the incompatibility with subprocessus. If `nixGL` is not installed in your stack environment, you can use `stack --nix --no-nix-pure exec ...`.
Contact me if this limitation is too important, it may be easy to automate this process.

View File

@ -2,28 +2,62 @@
let
pkgs = import <nixpkgs> { inherit system; };
version = "1.0.0";
in
with pkgs;
rec {
nixGl = pkgs.stdenv.mkDerivation rec {
name = "nixGL-${version}";
version = "1.0.0";
buildInputs = [ pkgs.python3 pkgs.which pkgs.binutils ];
outputs = [ "out" ];
src = ./.;
buildPhase = ''
mkdir -p $out/bin
'';
installPhase = ''
cp nixGL $out/bin
'';
nixGLNvidiaBumblebee = runCommand "nixGLNvidiaBumblebee-${version}" {
buildInputs = [ libglvnd linuxPackages.nvidia_x11 bumblebee ];
meta = with pkgs.stdenv.lib; {
description = "A tool to launch OpenGL application on system other than NixOS";
description = "A tool to launch OpenGL application on system other than NixOS - Nvidia bumblebee version";
homepage = "https://github.com/guibou/nixGL";
};
};
} ''
mkdir -p $out/bin
cat > $out/bin/nixGLNvidiaBumblebee << FOO
#!/usr/bin/env sh
export LD_LIBRARY_PATH=${linuxPackages.nvidia_x11}/lib
${bumblebee}/bin/optirun --ldpath ${libglvnd}/lib "\$@"
FOO
chmod u+x $out/bin/nixGLNvidiaBumblebee
'';
nixGLNvidia = runCommand "nixGLNvidia-${version}" {
buildInputs = [ libglvnd linuxPackages.nvidia_x11 ];
meta = with pkgs.stdenv.lib; {
description = "A tool to launch OpenGL application on system other than NixOS - Nvidia version";
homepage = "https://github.com/guibou/nixGL";
};
} ''
mkdir -p $out/bin
cat > $out/bin/nixGLNvidia << FOO
#!/usr/bin/env sh
export LD_LIBRARY_PATH=${linuxPackages.nvidia_x11}/lib:${libglvnd}/lib
"\$@"
FOO
chmod u+x $out/bin/nixGLNvidia
'';
nixGLIntel = runCommand "nixGLIntel-${version}" {
buildInputs = [ mesa_drivers ];
meta = with pkgs.stdenv.lib; {
description = "A tool to launch OpenGL application on system other than NixOS - Intel version";
homepage = "https://github.com/guibou/nixGL";
};
} ''
mkdir -p $out/bin
cat > $out/bin/nixGLIntel << FOO
#!/usr/bin/env sh
export LIBGL_DRIVERS_PATH=${mesa_drivers}/lib/dri
"\$@"
FOO
chmod u+x $out/bin/nixGLIntel
'';
}

50
nixGL
View File

@ -1,50 +0,0 @@
#!/usr/bin/env python
import sys
import os
import subprocess
# Any rpath with this pattern is ignored
# TODO: perhaps libOpenGL.so in nix exists in other packages, we
# should extend this list
ignored = [b'mesa-noglu', b'mesa']
# These are the list of system library which are added
# TODO: extend this list for other systems
systemLibs = [b"/usr/lib", b"/lib"]
# extract command line and real path of the program
cmd = (prog, *args) = sys.argv[1:]
realProg = subprocess.check_output(["which", prog]).strip()
# extract libs deps of the program
paths = []
for line in subprocess.check_output(['ldd', realProg]).split(b'\n'):
line = line.split()
if len(line) == 4:
lib = line[2]
path = os.path.dirname(lib)
for c in ignored:
if c in path:
break
else:
paths.append(path)
# build the new environment
newenv = os.environb
oldLdLibraryPath = newenv.get(b'LD_LIBRARY_PATH', b'').split()
# The build order is IMPORTANT
# first, any LD_LIBRARY_PATH from outside world. That's the default
# behavior, LD_LIBRARY_PATH has precedance over rpath
# Then, the different directory which satisfy our rpaths
# The the system library where the openGL lib can be located
# This ensure three properties:
# a) system library (and libOpenGL.so) are used AFTER the one of nix
# b) the executible respect the LD_LIBRARY_PATH for an user viewpoint
newLibraryPath = b':'.join(oldLdLibraryPath + paths + systemLibs)
newenv.update({b'LD_LIBRARY_PATH': newLibraryPath})
os.execvpe(prog, cmd, newenv)