From 446293584f10d56b91368f500c022f7a93edbe2c Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Mon, 29 Apr 2024 19:37:24 -0500 Subject: [PATCH 1/9] nixgl: add module This module enables wrapping programs which require access to libGL with nixGL on non-NixOS systems. --- modules/misc/news.nix | 12 ++++++++ modules/misc/nixgl.nix | 64 ++++++++++++++++++++++++++++++++++++++++++ modules/modules.nix | 1 + 3 files changed, 77 insertions(+) create mode 100644 modules/misc/nixgl.nix diff --git a/modules/misc/news.nix b/modules/misc/news.nix index 8f9a77cfa..801391cc2 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -1801,6 +1801,18 @@ in { itself. ''; } + + { + time = "2024-10-25T08:18:30+00:00"; + condition = hostPlatform.isLinux; + message = '' + A new module is available: 'nixGL'. + + NixGL solve the "OpenGL" problem with nix. The 'nixGL' module provides + integration of NixGL into Home Manager. See the "GPU on non-NixOS + systems" section in the Home Manager mantual for more. + ''; + } ]; }; } diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix new file mode 100644 index 000000000..b6cff7cc0 --- /dev/null +++ b/modules/misc/nixgl.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, ... }: + +let cfg = config.nixGL; +in { + meta.maintainers = [ lib.maintainers.smona ]; + + options.nixGL.prefix = lib.mkOption { + type = lib.types.str; + default = ""; + example = lib.literalExpression + ''"''${inputs.nixGL.packages.x86_64-linux.nixGLIntel}/bin/nixGLIntel"''; + description = '' + The nixGL command that `lib.nixGL.wrap` should wrap packages with. + This can be used to provide libGL access to applications on non-NixOS systems. + + Some packages are wrapped by default (e.g. kitty, firefox), but you can wrap other packages + as well, with `(config.lib.nixGL.wrap )`. If this option is empty (the default), + then `lib.nixGL.wrap` is a no-op. + ''; + }; + + config = { + lib.nixGL.wrap = # Wrap a single package with the configured nixGL wrapper + pkg: + + if cfg.prefix == "" then + pkg + else + # Wrap the package's binaries with nixGL, while preserving the rest of + # the outputs and derivation attributes. + (pkg.overrideAttrs (old: { + name = "nixGL-${pkg.name}"; + + # Make sure this is false for the wrapper derivation, so nix doesn't expect + # a new debug output to be produced. We won't be producing any debug info + # for the original package. + separateDebugInfo = false; + + buildCommand = '' + set -eo pipefail + + ${ + # Heavily inspired by https://stackoverflow.com/a/68523368/6259505 + pkgs.lib.concatStringsSep "\n" (map (outputName: '' + echo "Copying output ${outputName}" + set -x + cp -rs --no-preserve=mode "${ + pkg.${outputName} + }" "''$${outputName}" + set +x + '') (old.outputs or [ "out" ]))} + + rm -rf $out/bin/* + shopt -s nullglob # Prevent loop from running if no files + for file in ${pkg.out}/bin/*; do + echo "#!${pkgs.bash}/bin/bash" > "$out/bin/$(basename $file)" + echo "exec -a \"\$0\" ${cfg.prefix} $file \"\$@\"" >> "$out/bin/$(basename $file)" + chmod +x "$out/bin/$(basename $file)" + done + shopt -u nullglob # Revert nullglob back to its normal default state + ''; + })); + }; +} diff --git a/modules/modules.nix b/modules/modules.nix index ba8414721..3b4985083 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -31,6 +31,7 @@ let ./misc/gtk.nix ./misc/lib.nix ./misc/news.nix + ./misc/nixgl.nix ./misc/numlock.nix ./misc/pam.nix ./misc/qt.nix From bbd4254d00e8c69c4c958ddb51fb18637ca7f9b8 Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Wed, 19 Jun 2024 18:25:56 -0500 Subject: [PATCH 2/9] nixgl: make desktop files point to wrapped exe Some desktop files will refer to the absolute path of the original derivation, which would bypass nixGL wrapping. So we need to replace the path with the path to the wrapper derivation to ensure the wrapped version is always launched. --- modules/misc/nixgl.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index b6cff7cc0..16a00a755 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -57,6 +57,17 @@ in { echo "exec -a \"\$0\" ${cfg.prefix} $file \"\$@\"" >> "$out/bin/$(basename $file)" chmod +x "$out/bin/$(basename $file)" done + + # If .desktop files refer to the old package, replace the references + for dsk in "$out/share/applications"/*.desktop ; do + if ! grep "${pkg.out}" "$dsk" > /dev/null; then + continue + fi + src="$(readlink "$dsk")" + rm "$dsk" + sed "s|${pkg.out}|$out|g" "$src" > "$dsk" + done + shopt -u nullglob # Revert nullglob back to its normal default state ''; })); From b9fe747915d95c3ea37539cccea67d3df39526a9 Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Wed, 19 Jun 2024 18:35:53 -0500 Subject: [PATCH 3/9] nixgl: use makeWrapper and update docs makeWrapper is more consistent with the rest of nixpkgs & home-manager, so it should be a little more maintainable. It can also validate that the wrapper command is executable at build time. --- modules/misc/nixgl.nix | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index 16a00a755..c84397fc5 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -13,9 +13,9 @@ in { The nixGL command that `lib.nixGL.wrap` should wrap packages with. This can be used to provide libGL access to applications on non-NixOS systems. - Some packages are wrapped by default (e.g. kitty, firefox), but you can wrap other packages - as well, with `(config.lib.nixGL.wrap )`. If this option is empty (the default), - then `lib.nixGL.wrap` is a no-op. + Wrap individual packages like so: `(config.lib.nixGL.wrap )`. The returned package + can be used just like the original one, but will have access to libGL. If this option is empty (the default), + then `lib.nixGL.wrap` is a no-op. This is useful on NixOS, where the wrappers are unnecessary. ''; }; @@ -53,9 +53,12 @@ in { rm -rf $out/bin/* shopt -s nullglob # Prevent loop from running if no files for file in ${pkg.out}/bin/*; do - echo "#!${pkgs.bash}/bin/bash" > "$out/bin/$(basename $file)" - echo "exec -a \"\$0\" ${cfg.prefix} $file \"\$@\"" >> "$out/bin/$(basename $file)" - chmod +x "$out/bin/$(basename $file)" + local prog="$(basename "$file")" + makeWrapper \ + "${cfg.prefix}" \ + "$out/bin/$prog" \ + --argv0 "$prog" \ + --add-flags "$file" done # If .desktop files refer to the old package, replace the references From 199cf5634c2ed39fceae0da3b1d0a76f7d47e1b1 Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Wed, 26 Jun 2024 21:14:08 -0500 Subject: [PATCH 4/9] nixgl: use -q to silence grep Co-authored-by: V. <150687949+vigress8@users.noreply.github.com> --- modules/misc/nixgl.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index c84397fc5..76b05296b 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -63,7 +63,7 @@ in { # If .desktop files refer to the old package, replace the references for dsk in "$out/share/applications"/*.desktop ; do - if ! grep "${pkg.out}" "$dsk" > /dev/null; then + if ! grep -q "${pkg.out}" "$dsk"; then continue fi src="$(readlink "$dsk")" From d0c036ca4904701289e0a779253f241feeacbf40 Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Wed, 26 Jun 2024 21:16:22 -0500 Subject: [PATCH 5/9] nixgl: ensure makeWrapper is present during build Co-authored-by: V. <150687949+vigress8@users.noreply.github.com> --- modules/misc/nixgl.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index 76b05296b..90bd1f3aa 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -35,7 +35,7 @@ in { # a new debug output to be produced. We won't be producing any debug info # for the original package. separateDebugInfo = false; - + nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [ pkgs.makeWrapper ]; buildCommand = '' set -eo pipefail From 7dee0dc8f0c7d4f174c481f36d04b9edadba3b7e Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Mon, 1 Jul 2024 15:42:10 -0500 Subject: [PATCH 6/9] nixgl: reference lib directly Co-authored-by: V. <150687949+vigress8@users.noreply.github.com> --- modules/misc/nixgl.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index 90bd1f3aa..ca674ff13 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -41,7 +41,7 @@ in { ${ # Heavily inspired by https://stackoverflow.com/a/68523368/6259505 - pkgs.lib.concatStringsSep "\n" (map (outputName: '' + lib.concatStringsSep "\n" (map (outputName: '' echo "Copying output ${outputName}" set -x cp -rs --no-preserve=mode "${ From e61f87969ae179139164c7fb5e0bb76b791144e5 Mon Sep 17 00:00:00 2001 From: Mel Bourgeois Date: Fri, 9 Aug 2024 19:40:33 -0500 Subject: [PATCH 7/9] nixgl: Improve option documentation --- modules/misc/nixgl.nix | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index ca674ff13..47314b9c2 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -10,12 +10,28 @@ in { example = lib.literalExpression ''"''${inputs.nixGL.packages.x86_64-linux.nixGLIntel}/bin/nixGLIntel"''; description = '' - The nixGL command that `lib.nixGL.wrap` should wrap packages with. - This can be used to provide libGL access to applications on non-NixOS systems. + The [nixGL](https://github.com/nix-community/nixGL) command that `lib.nixGL.wrap` should prefix + package binaries with. nixGL provides your system's version of libGL to applications, enabling + them to access the GPU on non-NixOS systems. - Wrap individual packages like so: `(config.lib.nixGL.wrap )`. The returned package - can be used just like the original one, but will have access to libGL. If this option is empty (the default), - then `lib.nixGL.wrap` is a no-op. This is useful on NixOS, where the wrappers are unnecessary. + Wrap individual packages which require GPU access with the function like so: `(config.lib.nixGL.wrap )`. + The returned package can be used just like the original one, but will have access to libGL. For example: + + ```nix + # If you're using a Home Manager module to configure the package, + # pass it into the module's package argument: + programs.kitty = { + enable = true; + package = (config.lib.nixGL.wrap pkgs.kitty); + }; + + # Otherwise, pass it to any option where a package is expected: + home.packages = [ (config.lib.nixGL.wrap pkgs.hello) ]; + ``` + + If this option is empty (the default), then `lib.nixGL.wrap` is a no-op. This is useful for sharing your Home Manager + configurations between NixOS and non-NixOS systems, since NixOS already provides libGL to applications without the + need for nixGL. ''; }; @@ -35,7 +51,8 @@ in { # a new debug output to be produced. We won't be producing any debug info # for the original package. separateDebugInfo = false; - nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [ pkgs.makeWrapper ]; + nativeBuildInputs = old.nativeBuildInputs or [ ] + ++ [ pkgs.makeWrapper ]; buildCommand = '' set -eo pipefail From 7a5879707bb49c350aee7ab270c917584d430193 Mon Sep 17 00:00:00 2001 From: Jure Varlec Date: Wed, 10 Jul 2024 10:51:32 +0200 Subject: [PATCH 8/9] nixgl: API rework for flexibility and dual-GPU --- docs/manual/usage.md | 1 + docs/manual/usage/gpu-non-nixos.md | 70 ++++++++ modules/misc/nixgl.nix | 252 +++++++++++++++++++++++++---- 3 files changed, 288 insertions(+), 35 deletions(-) create mode 100644 docs/manual/usage/gpu-non-nixos.md diff --git a/docs/manual/usage.md b/docs/manual/usage.md index 2a569aaf9..88e884546 100644 --- a/docs/manual/usage.md +++ b/docs/manual/usage.md @@ -59,5 +59,6 @@ usage/configuration.md usage/rollbacks.md usage/dotfiles.md usage/graphical.md +usage/gpu-non-nixos.md usage/updating.md ``` diff --git a/docs/manual/usage/gpu-non-nixos.md b/docs/manual/usage/gpu-non-nixos.md new file mode 100644 index 000000000..7b7cfbaea --- /dev/null +++ b/docs/manual/usage/gpu-non-nixos.md @@ -0,0 +1,70 @@ +# GPU on non-NixOS systems {#sec-usage-gpu-non-nixos} + +To access the GPU, programs need access to OpenGL and Vulkan libraries. While +this works transparently on NixOS, it does not on other Linux systems. A +solution is provided by [NixGL](https://github.com/nix-community/nixGL), which +can be integrated into Home Manager. + +To enable the integration, import NixGL into your home configuration, either as +a channel, or as a flake input passed via `extraSpecialArgs`. Then, set the +`nixGL.packages` option to the package set provided by NixGL. + +Once integration is enabled, it can be used in two ways: as Nix functions for +wrapping programs installed via Home Manager, and as shell commands for running +programs installed by other means (such as `nix shell`). In either case, there +are several wrappers available. They can be broadly categorized + +- by vendor: as Mesa (for Free drivers of all vendors) and Nvidia (for + Nvidia-specific proprietary drivers). +- by GPU selection: as primary and secondary (offloading). + +For example, the `mesa` wrapper provides support for running programs on the +primary GPU for Intel, AMD and Nouveau drivers, while the `mesaPrime` wrapper +does the same for the secondary GPU. + +**Note:** when using Nvidia wrappers together with flakes, your home +configuration will not be pure and needs to be built using `home-manager switch +--impure`. Otherwise, the build will fail, complaining about missing attribute +`currentTime`. + +Wrapper functions are available under `config.lib.nixGL.wrappers`. However, it +can be more convenient to use the `config.lib.nixGL.wrap` alias, which can be +configured to use any of the wrappers. It is intended to provide a customization +point when the same home configuration is used across several machines with +different hardware. There is also the `config.lib.nixGL.wrapOffload` alias for +two-GPU systems. + +Another convenience is that all wrapper functions are always available. However, +when `nixGL.packages` option is unset, they are no-ops. This allows them to be +used even when the home configuration is used on NixOS machines. The exception +is the `prime-offload` script which ignores `nixGL.packages` and is installed +into the environment whenever `nixGL.prime.installScript` is set. This script, +which can be used to start a program on a secondary GPU, does not depend on +NixGL and is useful on NixOS systems as well. + +Below is an abbreviated example for an Optimus laptop that makes use of both +Mesa and Nvidia wrappers, where the latter is used in dGPU offloading mode. It +demonstrates how to wrap `mpv` to run on the integrated Intel GPU, wrap FreeCAD +to run on the Nvidia dGPU, and how to install the wrapper scripts. It also wraps +Xonotic to run on the dGPU, but uses the wrapper function directly for +demonstration purposes. + +```nix +{ config, lib, pkgs, nixGL, ... }: +{ + nixGL.packages = nixGL.packages; + nixGL.defaultWrapper = "mesa"; + nixGL.offloadWrapper = "nvidiaPrime"; + nixGL.installScripts = [ "mesa" "nvidiaPrime" ]; + + programs.mpv = { + enable = true; + package = config.lib.nixGL.wrap pkgs.mpv; + }; + + home.packages = [ + (config.lib.nixGL.wrapOffload pkgs.freecad) + (config.lib.nixGL.wrappers.nvidiaPrime pkgs.xonotic) + ]; +} +``` diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index 47314b9c2..f45c483bc 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -1,45 +1,166 @@ { config, lib, pkgs, ... }: -let cfg = config.nixGL; +let + cfg = config.nixGL; + wrapperListMarkdown = with builtins; + foldl' (list: name: + list + '' + - ${name} + '') "" (attrNames config.lib.nixGL.wrappers); in { meta.maintainers = [ lib.maintainers.smona ]; - options.nixGL.prefix = lib.mkOption { - type = lib.types.str; - default = ""; - example = lib.literalExpression - ''"''${inputs.nixGL.packages.x86_64-linux.nixGLIntel}/bin/nixGLIntel"''; - description = '' - The [nixGL](https://github.com/nix-community/nixGL) command that `lib.nixGL.wrap` should prefix - package binaries with. nixGL provides your system's version of libGL to applications, enabling - them to access the GPU on non-NixOS systems. + options.nixGL = { + packages = lib.mkOption { + type = with lib.types; nullOr attrs; + default = null; + example = lib.literalExpression "inputs.nixGL.packages"; + description = '' + The nixGL package set containing GPU library wrappers. This can be used + to provide OpenGL and Vulkan access to applications on non-NixOS systems + by using `(config.lib.nixGL.wrap )` for the default wrapper, or + `(config.lib.nixGL.wrappers. )` for any available + wrapper. - Wrap individual packages which require GPU access with the function like so: `(config.lib.nixGL.wrap )`. - The returned package can be used just like the original one, but will have access to libGL. For example: + The wrapper functions are always available. If this option is empty (the + default), they are a no-op. This is useful on NixOS where the wrappers + are unnecessary. - ```nix - # If you're using a Home Manager module to configure the package, - # pass it into the module's package argument: - programs.kitty = { - enable = true; - package = (config.lib.nixGL.wrap pkgs.kitty); - }; + Note that using any Nvidia wrapper requires building the configuration + with the `--impure` option. + ''; + }; - # Otherwise, pass it to any option where a package is expected: - home.packages = [ (config.lib.nixGL.wrap pkgs.hello) ]; - ``` + defaultWrapper = lib.mkOption { + type = lib.types.enum (builtins.attrNames config.lib.nixGL.wrappers); + default = "mesa"; + description = '' + The package wrapper function available for use as `(config.lib.nixGL.wrap + )`. Intended to start programs on the main GPU. - If this option is empty (the default), then `lib.nixGL.wrap` is a no-op. This is useful for sharing your Home Manager - configurations between NixOS and non-NixOS systems, since NixOS already provides libGL to applications without the - need for nixGL. - ''; + Wrapper functions can be found under `config.lib.nixGL.wrappers`. They + can be used directly, however, setting this option provides a convenient + shorthand. + + The following wrappers are available: + ${wrapperListMarkdown} + ''; + }; + + offloadWrapper = lib.mkOption { + type = lib.types.enum (builtins.attrNames config.lib.nixGL.wrappers); + default = "mesaPrime"; + description = '' + The package wrapper function available for use as + `(config.lib.nixGL.wrapOffload )`. Intended to start programs + on the secondary GPU. + + Wrapper functions can be found under `config.lib.nixGL.wrappers`. They + can be used directly, however, setting this option provides a convenient + shorthand. + + The following wrappers are available: + ${wrapperListMarkdown} + ''; + }; + + prime.card = lib.mkOption { + type = lib.types.str; + default = "1"; + example = "pci-0000_06_00_0"; + description = '' + Selects the non-default graphics card used for PRIME render offloading. + The value can be: + + - a number, selecting the n-th non-default GPU; + - a PCI bus id in the form `pci-XXX_YY_ZZ_U`; + - a PCI id in the form `vendor_id:device_id` + + For more information, consult the Mesa documentation on the `DRI_PRIME` + environment variable. + ''; + }; + + prime.nvidiaProvider = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + example = "NVIDIA-G0"; + description = '' + If this option is set, it overrides the offload provider for Nvidia + PRIME offloading. Consult the proprietary Nvidia driver documentation + on the `__NV_PRIME_RENDER_OFFLOAD_PROVIDER` environment variable. + ''; + }; + + prime.installScript = lib.mkOption { + type = with lib.types; nullOr (enum [ "mesa" "nvidia" ]); + default = null; + example = "mesa"; + description = '' + If this option is set, the wrapper script `prime-offload` is installed + into the environment. It allows starting programs on the secondary GPU + selected by the `nixGL.prime.card` option. This makes sense when the + program is not already using one of nixGL PRIME wrappers, or for + programs not installed from Nixpkgs. + + This option can be set to either "mesa" or "nvidia", making the script + use one or the other graphics library. + ''; + }; + + installScripts = lib.mkOption { + type = with lib.types; + nullOr (listOf (enum (builtins.attrNames config.lib.nixGL.wrappers))); + default = null; + example = [ "mesa" "mesaPrime" ]; + description = '' + For each wrapper `wrp` named in the provided list, a wrapper script + named `nixGLWrp` is installed into the environment. These scripts are + useful for running programs not installed via Home Manager. + + The following wrappers are available: + ${wrapperListMarkdown} + ''; + }; + + vulkan.enable = lib.mkOption { + type = lib.types.bool; + default = false; + example = true; + description = '' + Whether to enable Vulkan in nixGL wrappers. + + This is disabled by default bacause Vulkan brings in several libraries + that can cause symbol version conflicts in wrapped programs. Your + mileage may vary. + ''; + }; }; - config = { - lib.nixGL.wrap = # Wrap a single package with the configured nixGL wrapper - pkg: + config = let + getWrapperExe = vendor: + let + glPackage = cfg.packages.${pkgs.system}."nixGL${vendor}"; + glExe = lib.getExe glPackage; + vulkanPackage = cfg.packages.${pkgs.system}."nixVulkan${vendor}"; + vulkanExe = if cfg.vulkan.enable then lib.getExe vulkanPackage else ""; + in "${glExe} ${vulkanExe}"; - if cfg.prefix == "" then + mesaOffloadEnv = { "DRI_PRIME" = "${cfg.prime.card}"; }; + + nvOffloadEnv = { + "DRI_PRIME" = "${cfg.prime.card}"; + "__NV_PRIME_RENDER_OFFLOAD" = "1"; + "__GLX_VENDOR_LIBRARY_NAME" = "nvidia"; + "__VK_LAYER_NV_optimus" = "NVIDIA_only"; + } // (let provider = cfg.prime.nvidiaProvider; + in if !isNull provider then { + "__NV_PRIME_RENDER_OFFLOAD_PROVIDER" = "${provider}"; + } else + { }); + + makePackageWrapper = vendor: environment: pkg: + if builtins.isNull cfg.packages then pkg else # Wrap the package's binaries with nixGL, while preserving the rest of @@ -53,11 +174,17 @@ in { separateDebugInfo = false; nativeBuildInputs = old.nativeBuildInputs or [ ] ++ [ pkgs.makeWrapper ]; - buildCommand = '' + buildCommand = let + # We need an intermediate wrapper package because makeWrapper + # requires a single executable as the wrapper. + combinedWrapperPkg = + pkgs.writeShellScriptBin "nixGLCombinedWrapper-${vendor}" '' + exec ${getWrapperExe vendor} "$@" + ''; + in '' set -eo pipefail - ${ - # Heavily inspired by https://stackoverflow.com/a/68523368/6259505 + ${ # Heavily inspired by https://stackoverflow.com/a/68523368/6259505 lib.concatStringsSep "\n" (map (outputName: '' echo "Copying output ${outputName}" set -x @@ -72,10 +199,14 @@ in { for file in ${pkg.out}/bin/*; do local prog="$(basename "$file")" makeWrapper \ - "${cfg.prefix}" \ + "${lib.getExe combinedWrapperPkg}" \ "$out/bin/$prog" \ --argv0 "$prog" \ - --add-flags "$file" + --add-flags "$file" \ + ${ + lib.concatStringsSep " " (lib.attrsets.mapAttrsToList + (var: val: "--set '${var}' '${val}'") environment) + } done # If .desktop files refer to the old package, replace the references @@ -91,5 +222,56 @@ in { shopt -u nullglob # Revert nullglob back to its normal default state ''; })); + + wrappers = { + mesa = makePackageWrapper "Intel" { }; + mesaPrime = makePackageWrapper "Intel" mesaOffloadEnv; + nvidia = makePackageWrapper "Nvidia" { }; + nvidiaPrime = makePackageWrapper "Nvidia" nvOffloadEnv; + }; + in { + lib.nixGL.wrap = wrappers.${cfg.defaultWrapper}; + lib.nixGL.wrapOffload = wrappers.${cfg.offloadWrapper}; + lib.nixGL.wrappers = wrappers; + + home.packages = let + wantsPrimeWrapper = (!isNull cfg.prime.installScript); + wantsWrapper = wrapper: + (!isNull cfg.packages) && (!isNull cfg.installScripts) + && (builtins.elem wrapper cfg.installScripts); + envVarsAsScript = environment: + lib.concatStringsSep "\n" + (lib.attrsets.mapAttrsToList (var: val: "export ${var}=${val}") + environment); + in [ + (lib.mkIf wantsPrimeWrapper (pkgs.writeShellScriptBin "prime-offload" '' + ${if cfg.prime.installScript == "mesa" then + (envVarsAsScript mesaOffloadEnv) + else + (envVarsAsScript nvOffloadEnv)} + exec "$@" + '')) + + (lib.mkIf (wantsWrapper "mesa") (pkgs.writeShellScriptBin "nixGLMesa" '' + exec ${getWrapperExe "Intel"} "$@" + '')) + + (lib.mkIf (wantsWrapper "mesaPrime") + (pkgs.writeShellScriptBin "nixGLMesaPrime" '' + ${envVarsAsScript mesaOffloadEnv} + exec ${getWrapperExe "Intel"} "$@" + '')) + + (lib.mkIf (wantsWrapper "nvidia") + (pkgs.writeShellScriptBin "nixGLNvidia" '' + exec ${getWrapperExe "Nvidia"} "$@" + '')) + + (lib.mkIf (wantsWrapper "nvidia") + (pkgs.writeShellScriptBin "nixGLNvidiaPrime" '' + ${envVarsAsScript nvOffloadEnv} + exec ${getWrapperExe "Nvidia"} "$@" + '')) + ]; }; } From 8bd6e0a1a805c373686e21678bb07f23293d357b Mon Sep 17 00:00:00 2001 From: Jure Varlec Date: Wed, 2 Oct 2024 20:19:53 +0200 Subject: [PATCH 9/9] nixgl: add support for channel-based configuration --- docs/manual/usage/gpu-non-nixos.md | 15 +++++++++++++-- modules/misc/nixgl.nix | 23 +++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/docs/manual/usage/gpu-non-nixos.md b/docs/manual/usage/gpu-non-nixos.md index 7b7cfbaea..0aefa4ae3 100644 --- a/docs/manual/usage/gpu-non-nixos.md +++ b/docs/manual/usage/gpu-non-nixos.md @@ -50,9 +50,9 @@ Xonotic to run on the dGPU, but uses the wrapper function directly for demonstration purposes. ```nix -{ config, lib, pkgs, nixGL, ... }: +{ config, lib, pkgs, nixgl, ... }: { - nixGL.packages = nixGL.packages; + nixGL.packages = nixgl.packages; nixGL.defaultWrapper = "mesa"; nixGL.offloadWrapper = "nvidiaPrime"; nixGL.installScripts = [ "mesa" "nvidiaPrime" ]; @@ -68,3 +68,14 @@ demonstration purposes. ]; } ``` + +The above example assumes a flake-based setup where `nixgl` was passed from the +flake. When using channels, the example would instead begin with + +```nix +{ config, lib, pkgs, ... }: +{ + nixGL.packages = import { inherit pkgs; }; + # The rest is the same as above + ... +``` diff --git a/modules/misc/nixgl.nix b/modules/misc/nixgl.nix index f45c483bc..2c7fcaffc 100644 --- a/modules/misc/nixgl.nix +++ b/modules/misc/nixgl.nix @@ -138,11 +138,30 @@ in { }; config = let + findWrapperPackage = packageAttr: + # NixGL has wrapper packages in different places depending on how you + # access it. We want HM configuration to be the same, regardless of how + # NixGL is imported. + # + # First, let's see if we have a flake. + if builtins.hasAttr pkgs.system cfg.packages then + cfg.packages.${pkgs.system}.${packageAttr} + else + # Next, let's see if we have a channel. + if builtins.hasAttr packageAttr cfg.packages then + cfg.packages.${packageAttr} + else + # Lastly, with channels, some wrappers are grouped under "auto". + if builtins.hasAttr "auto" cfg.packages then + cfg.packages.auto.${packageAttr} + else + throw "Incompatible NixGL package layout"; + getWrapperExe = vendor: let - glPackage = cfg.packages.${pkgs.system}."nixGL${vendor}"; + glPackage = findWrapperPackage "nixGL${vendor}"; glExe = lib.getExe glPackage; - vulkanPackage = cfg.packages.${pkgs.system}."nixVulkan${vendor}"; + vulkanPackage = findWrapperPackage "nixVulkan${vendor}"; vulkanExe = if cfg.vulkan.enable then lib.getExe vulkanPackage else ""; in "${glExe} ${vulkanExe}";