Rust, Bevy, Vulkan Loader and LD_LIBRARY_PATH variable

TLDR: Is there a way to make Bevy program work on NixOS without LD_LIBRARY_PATH pointing to vulkan-loader?

In a sense this is a continuation of the discussion at Unable to find GPU, but there we focused on development environment, here I’m interested in distribution to end users.

I’m playing with Bevy (Rust game engine) again and again running into Unable to find a GPU! runtime error. It can be solved by adding vulkan-loader to LD_LIBRARY_PATH but I think there is something wrong with this approach.

Setting this variable in my development environment with Nix is fine, but the built program depends on this variable too. If I run it outside the development shell, it crashes, unless I set this variable to contain vulkan-loader shared objects.

For now I did it with home-manager session-variables like this:

home.sessionVariables = {
  LD_LIBRARY_PATH = "\$\{LD_LIBRARY_PATH\}:${ pkgs.lib.makeLibraryPath [ pkgs.vulkan-loader ] }";
}

and I can run the program on my system, but obviously this is not good for other users. For example, on my own system I get this:

$ unset LD_LIBRARY_PATH
$ nix run 'gitlab:tad-lispy/bevy-lion-playground?rev=e29c4f5f7d23a7c6f6d93a9ad81b52599f8ed0ba'
thread 'main' panicked at 'Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/87722d135fcc7e6058aa4710e7fe5be5f2a6e594/docs/linux_dependencies.md', /build/cargo-vendor-dir/bevy_render-0.9.1/src/renderer/mod.rs:121:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

If I want to distribute my program to other NixOS users via flake, how should I deal with it?

1 Like

I was messing around with wgpu (specifically, gpgpu) in Rust a little while ago and found that I needed to have dontPatchELF = true in my Nix expression. Basically, vulkan-loader was being added to the executable’s RPATH, but stdenv was removing it in the fixup phase because there was no direct shared library dependency on it (I guess it’s loaded with dlopen). I ended up with a nix expression like this:

{ stdenv
, rustPlatform
, vulkan-loader
, libX11
, libXcursor
, libXrandr
, libXi
, darwin
, lib
}:

rustPlatform.buildRustPackage {
  pname = "foobar";
  version = "0.1.0";

  src = ./foobar;
  cargoLock = {
    lockFile = ./foobar/Cargo.lock;
    outputHashes."gpgpu-0.2.0" = "sha256-qPMPmIWvxPeRFXtzgee+Pbiu65C0q76zPlVBPfivbzE=";
  };

  buildInputs = lib.optionals stdenv.isLinux [
    vulkan-loader
    libX11
    libXcursor
    libXrandr
    libXi
  ] ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [
    QuartzCore
  ]);

  dontPatchELF = true;
}
2 Likes

Thanks! The dontPatchElf option didn’t help, but you put me on right path and this seems to work:

postFixup = ''
  patchelf --add-rpath ${ pkgs.vulkan-loader }/lib $out/bin/*
'';
1 Like

dontPatchElf

Note that it’s dontPatchELF, not dontPatchElf. Also you do need to make sure you’ve got the necessary stuff in buildInputs. The list in my code block above was what I had to use to get gpgpu working.

Yeah, my mistake. Thanks.

I’m pretty sure the correct form (dontPatchELF) is what I had in my nix file. Anyway, adding this one path after the fixup seems better than skipping patchelf altogether.

Also, in my case it seems like the rpath is not there to begin with, as even running the program built with cargo (without nix build or patchelf involved) gives me the same error.

$ cargo build --release
    Finished release [optimized] target(s) in 0.24s

$ patchelf --print-rpath target/release/bevy-lyon-playground | sed 's/\:/\n/g'
/home/tad/Projects/bevy-lyon-playground/outputs/out/lib64
/home/tad/Projects/bevy-lyon-playground/outputs/out/lib
/nix/store/rz45273l509rqn3m61pcgc26kaj41nj5-alsa-lib-1.2.7.2/lib
/nix/store/wm0fbn9fg03b1xmaqs6zxxp66yqfp0qh-systemd-252.4/lib
/nix/store/34g510ipxdkznlfscg8mpy58kjd3iyck-libxkbcommon-1.5.0/lib
/nix/store/ijk1lx8cq3sjpbpbl7sjz3218q9waqwz-wayland-1.21.0/lib
/nix/store/c35hf8g5b9vksadym9dbjrd6p2y11m8h-glibc-2.35-224/lib
/nix/store/qbgfsaviwqi2p6jr7an1g2754sv3xqhn-gcc-11.3.0-lib/lib

I’m a bit of a monkey when it comes to compilers, linkers and other systems programming stuff, but I guess lack of vulkan-loader in the rpath is the problem, right? And this executable was not patched.

bevy has a nix section that should work out of the box

2 Likes

The link to Jumpy derivation is very helpful and I see it is indeed adding the vulkan-loader to rpath. Maybe this should be explicitly mentioned in the NixOS section of the Bevy document? Lots of other stuff going on there that I can learn from. Thanks!

IIUC the Bevy document is more about developing, that’s why it only briefly mentions packaging with the jumpy example. The bevy document adds vulkan-loader (as part of buildInputs) to LD_LIBRARY_PATH instead, since it is easier to use when developing.