mirror of
https://github.com/nix-community/home-manager
synced 2024-12-12 21:09:49 +01:00
296 lines
10 KiB
Nix
296 lines
10 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
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 = {
|
|
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 <package>)` for the default wrapper, or
|
|
`(config.lib.nixGL.wrappers.<wrapper> <package>)` for any available
|
|
wrapper.
|
|
|
|
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.
|
|
|
|
Note that using any Nvidia wrapper requires building the configuration
|
|
with the `--impure` option.
|
|
'';
|
|
};
|
|
|
|
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
|
|
<package>)`. Intended to start programs on the main 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}
|
|
'';
|
|
};
|
|
|
|
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 <package>)`. 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 = 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 = findWrapperPackage "nixGL${vendor}";
|
|
glExe = lib.getExe glPackage;
|
|
vulkanPackage = findWrapperPackage "nixVulkan${vendor}";
|
|
vulkanExe = if cfg.vulkan.enable then lib.getExe vulkanPackage else "";
|
|
in "${glExe} ${vulkanExe}";
|
|
|
|
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
|
|
# 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;
|
|
nativeBuildInputs = old.nativeBuildInputs or [ ]
|
|
++ [ pkgs.makeWrapper ];
|
|
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
|
|
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
|
|
local prog="$(basename "$file")"
|
|
makeWrapper \
|
|
"${lib.getExe combinedWrapperPkg}" \
|
|
"$out/bin/$prog" \
|
|
--argv0 "$prog" \
|
|
--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
|
|
for dsk in "$out/share/applications"/*.desktop ; do
|
|
if ! grep -q "${pkg.out}" "$dsk"; 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
|
|
'';
|
|
}));
|
|
|
|
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"} "$@"
|
|
''))
|
|
];
|
|
};
|
|
}
|