Merge pull request #42846 from ambrop72/optimus-prime-config-master
nixos/xserver: Implement configuration of NVIDIA Optimus via PRIME
This commit is contained in:
commit
1ffe83caa7
|
@ -26,9 +26,73 @@ let
|
|||
nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; };
|
||||
|
||||
enabled = nvidia_x11 != null;
|
||||
|
||||
cfg = config.hardware.nvidia;
|
||||
optimusCfg = cfg.optimus_prime;
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
hardware.nvidia.modesetting.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable kernel modesetting when using the NVIDIA proprietary driver.
|
||||
|
||||
Enabling this fixes screen tearing when using Optimus via PRIME (see
|
||||
<option>hardware.nvidia.optimus_prime.enable</option>. This is not enabled
|
||||
by default because it is not officially supported by NVIDIA and would not
|
||||
work with SLI.
|
||||
'';
|
||||
};
|
||||
|
||||
hardware.nvidia.optimus_prime.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
|
||||
If enabled, the NVIDIA GPU will be always on and used for all rendering,
|
||||
while enabling output to displays attached only to the integrated Intel GPU
|
||||
without a multiplexer.
|
||||
|
||||
Note that this option only has any effect if the "nvidia" driver is specified
|
||||
in <option>services.xserver.videoDrivers</option>, and it should preferably
|
||||
be the only driver there.
|
||||
|
||||
If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
|
||||
specified (<option>hardware.nvidia.optimus_prime.nvidiaBusId</option> and
|
||||
<option>hardware.nvidia.optimus_prime.intelBusId</option>).
|
||||
|
||||
If you enable this, you may want to also enable kernel modesetting for the
|
||||
NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order
|
||||
to prevent tearing.
|
||||
|
||||
Note that this configuration will only be successful when a display manager
|
||||
for which the <option>services.xserver.displayManager.setupCommands</option>
|
||||
option is supported is used; notably, SLiM is not supported.
|
||||
'';
|
||||
};
|
||||
|
||||
hardware.nvidia.optimus_prime.nvidiaBusId = lib.mkOption {
|
||||
type = lib.types.string;
|
||||
default = "";
|
||||
example = "PCI:1:0:0";
|
||||
description = ''
|
||||
Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
|
||||
shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
|
||||
'';
|
||||
};
|
||||
|
||||
hardware.nvidia.optimus_prime.intelBusId = lib.mkOption {
|
||||
type = lib.types.string;
|
||||
default = "";
|
||||
example = "PCI:0:2:0";
|
||||
description = ''
|
||||
Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
|
||||
shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf enabled {
|
||||
assertions = [
|
||||
|
@ -36,15 +100,61 @@ in
|
|||
assertion = config.services.xserver.displayManager.gdm.wayland;
|
||||
message = "NVidia drivers don't support wayland";
|
||||
}
|
||||
{
|
||||
assertion = !optimusCfg.enable ||
|
||||
(optimusCfg.nvidiaBusId != "" && optimusCfg.intelBusId != "");
|
||||
message = ''
|
||||
When NVIDIA Optimus via PRIME is enabled, the GPU bus IDs must configured.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
services.xserver.drivers = singleton
|
||||
{ name = "nvidia"; modules = [ nvidia_x11.bin ]; libPath = [ nvidia_x11 ]; };
|
||||
# If Optimus/PRIME is enabled, we:
|
||||
# - Specify the configured NVIDIA GPU bus ID in the Device section for the
|
||||
# "nvidia" driver.
|
||||
# - Add the AllowEmptyInitialConfiguration option to the Screen section for the
|
||||
# "nvidia" driver, in order to allow the X server to start without any outputs.
|
||||
# - Add a separate Device section for the Intel GPU, using the "modesetting"
|
||||
# driver and with the configured BusID.
|
||||
# - Reference that Device section from the ServerLayout section as an inactive
|
||||
# device.
|
||||
# - Configure the display manager to run specific `xrandr` commands which will
|
||||
# configure/enable displays connected to the Intel GPU.
|
||||
|
||||
services.xserver.screenSection =
|
||||
services.xserver.drivers = singleton {
|
||||
name = "nvidia";
|
||||
modules = [ nvidia_x11.bin ];
|
||||
libPath = [ nvidia_x11 ];
|
||||
deviceSection = optionalString optimusCfg.enable
|
||||
''
|
||||
BusID "${optimusCfg.nvidiaBusId}"
|
||||
'';
|
||||
screenSection =
|
||||
''
|
||||
Option "RandRRotation" "on"
|
||||
${optionalString optimusCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
|
||||
'';
|
||||
};
|
||||
|
||||
services.xserver.extraConfig = optionalString optimusCfg.enable
|
||||
''
|
||||
Option "RandRRotation" "on"
|
||||
Section "Device"
|
||||
Identifier "nvidia-optimus-intel"
|
||||
Driver "modesetting"
|
||||
BusID "${optimusCfg.intelBusId}"
|
||||
Option "AccelMethod" "none"
|
||||
EndSection
|
||||
'';
|
||||
services.xserver.serverLayoutSection = optionalString optimusCfg.enable
|
||||
''
|
||||
Inactive "nvidia-optimus-intel"
|
||||
'';
|
||||
|
||||
services.xserver.displayManager.setupCommands = optionalString optimusCfg.enable ''
|
||||
# Added by nvidia configuration module for Optimus/PRIME.
|
||||
${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource modesetting NVIDIA-0
|
||||
${pkgs.xorg.xrandr}/bin/xrandr --auto
|
||||
'';
|
||||
|
||||
environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
|
||||
source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
|
||||
|
@ -62,6 +172,8 @@ in
|
|||
boot.kernelModules = [ "nvidia-uvm" ] ++
|
||||
lib.optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
|
||||
|
||||
# If requested enable modesetting via kernel parameter.
|
||||
boot.kernelParams = optional cfg.modesetting.enable "nvidia-drm.modeset=1";
|
||||
|
||||
# Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
|
||||
services.udev.extraRules =
|
||||
|
|
|
@ -222,6 +222,17 @@ in
|
|||
description = "List of arguments for the X server.";
|
||||
};
|
||||
|
||||
setupCommands = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed just after the X server has started.
|
||||
|
||||
This option is only effective for display managers for which this feature
|
||||
is supported; currently these are LightDM, GDM and SDDM.
|
||||
'';
|
||||
};
|
||||
|
||||
sessionCommands = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
|
|
|
@ -7,6 +7,13 @@ let
|
|||
cfg = config.services.xserver.displayManager;
|
||||
gdm = pkgs.gnome3.gdm;
|
||||
|
||||
xSessionWrapper = if (cfg.setupCommands == "") then null else
|
||||
pkgs.writeScript "gdm-x-session-wrapper" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
${cfg.setupCommands}
|
||||
exec "$@"
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
@ -112,6 +119,11 @@ in
|
|||
GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions";
|
||||
# Find the mouse
|
||||
XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons";
|
||||
} // optionalAttrs (xSessionWrapper != null) {
|
||||
# Make GDM use this wrapper before running the session, which runs the
|
||||
# configured setupCommands. This relies on a patched GDM which supports
|
||||
# this environment variable.
|
||||
GDM_X_SESSION_WRAPPER = "${xSessionWrapper}";
|
||||
};
|
||||
execCmd = "exec ${gdm}/bin/gdm";
|
||||
};
|
||||
|
|
|
@ -62,6 +62,12 @@ let
|
|||
${optionalString hasDefaultUserSession ''
|
||||
user-session=${defaultSessionName}
|
||||
''}
|
||||
${optionalString (dmcfg.setupCommands != "") ''
|
||||
display-setup-script=${pkgs.writeScript "lightdm-display-setup" ''
|
||||
#!${pkgs.bash}/bin/bash
|
||||
${dmcfg.setupCommands}
|
||||
''}
|
||||
''}
|
||||
${cfg.extraSeatDefaults}
|
||||
'';
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ let
|
|||
Xsetup = pkgs.writeScript "Xsetup" ''
|
||||
#!/bin/sh
|
||||
${cfg.setupScript}
|
||||
${dmcfg.setupCommands}
|
||||
'';
|
||||
|
||||
Xstop = pkgs.writeScript "Xstop" ''
|
||||
|
@ -137,7 +138,8 @@ in
|
|||
xrandr --auto
|
||||
'';
|
||||
description = ''
|
||||
A script to execute when starting the display server.
|
||||
A script to execute when starting the display server. DEPRECATED, please
|
||||
use <option>services.xserver.displayManager.setupCommands</option>.
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
|
@ -374,6 +374,12 @@ in
|
|||
description = "Contents of the first Monitor section of the X server configuration file.";
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Additional contents (sections) included in the X server configuration file";
|
||||
};
|
||||
|
||||
xrandrHeads = mkOption {
|
||||
default = [];
|
||||
example = [
|
||||
|
@ -754,6 +760,7 @@ in
|
|||
Driver "${driver.driverName or driver.name}"
|
||||
${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
|
||||
${cfg.deviceSection}
|
||||
${driver.deviceSection or ""}
|
||||
${xrandrDeviceSection}
|
||||
EndSection
|
||||
|
||||
|
@ -765,6 +772,7 @@ in
|
|||
''}
|
||||
|
||||
${cfg.screenSection}
|
||||
${driver.screenSection or ""}
|
||||
|
||||
${optionalString (cfg.defaultDepth != 0) ''
|
||||
DefaultDepth ${toString cfg.defaultDepth}
|
||||
|
@ -794,6 +802,8 @@ in
|
|||
'')}
|
||||
|
||||
${xrandrMonitorSections}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
fonts.enableDefaultFonts = mkDefault true;
|
||||
|
|
|
@ -37,13 +37,27 @@ stdenv.mkDerivation rec {
|
|||
|
||||
# Disable Access Control because our X does not support FamilyServerInterpreted yet
|
||||
patches = [
|
||||
# Change hardcoded paths to nix store paths.
|
||||
(substituteAll {
|
||||
src = ./fix-paths.patch;
|
||||
inherit coreutils plymouth xwayland;
|
||||
})
|
||||
|
||||
# The following patches implement certain environment variables in GDM which are set by
|
||||
# the gdm configuration module (nixos/modules/services/x11/display-managers/gdm.nix).
|
||||
|
||||
# Look for session definition files in the directory specified by GDM_SESSIONS_DIR.
|
||||
./sessions_dir.patch
|
||||
|
||||
# Allow specifying X server arguments with GDM_X_SERVER_EXTRA_ARGS.
|
||||
./gdm-x-session_extra_args.patch
|
||||
./gdm-session-worker_xserver-path.patch
|
||||
|
||||
# Allow specifying a wrapper for running the session command.
|
||||
./gdm-x-session_session-wrapper.patch
|
||||
|
||||
# Forwards certain environment variables to the gdm-x-session child process
|
||||
# to ensure that the above two patches actually work.
|
||||
./gdm-session-worker_forward-vars.patch
|
||||
];
|
||||
|
||||
installFlags = [
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
|
||||
index 9ef4c5b..94da834 100644
|
||||
--- a/daemon/gdm-session-worker.c
|
||||
+++ b/daemon/gdm-session-worker.c
|
||||
@@ -1515,6 +1515,16 @@ gdm_session_worker_load_env_d (GdmSessionWorker *worker)
|
||||
g_object_unref (dir);
|
||||
}
|
||||
|
||||
+static void
|
||||
+gdm_session_worker_forward_var (GdmSessionWorker *worker, char const *var)
|
||||
+{
|
||||
+ char const *value = g_getenv(var);
|
||||
+ if (value != NULL) {
|
||||
+ g_debug ("forwarding %s= %s", var, value);
|
||||
+ gdm_session_worker_set_environment_variable(worker, var, value);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static gboolean
|
||||
gdm_session_worker_accredit_user (GdmSessionWorker *worker,
|
||||
GError **error)
|
||||
@@ -1559,6 +1569,9 @@ gdm_session_worker_accredit_user (GdmSessionWorker *worker,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ gdm_session_worker_forward_var(worker, "GDM_X_SERVER_EXTRA_ARGS");
|
||||
+ gdm_session_worker_forward_var(worker, "GDM_X_SESSION_WRAPPER");
|
||||
+
|
||||
gdm_session_worker_update_environment_from_passwd_info (worker,
|
||||
uid,
|
||||
gid,
|
|
@ -1,17 +0,0 @@
|
|||
diff --git a/daemon/gdm-session-worker.c.orig b/daemon/gdm-session-worker.c
|
||||
index 7bbda49..592691d 100644
|
||||
--- a/daemon/gdm-session-worker.c.orig
|
||||
+++ b/daemon/gdm-session-worker.c
|
||||
@@ -1557,6 +1557,12 @@ gdm_session_worker_accredit_user (GdmSessionWorker *worker,
|
||||
goto out;
|
||||
}
|
||||
|
||||
+ if (g_getenv ("GDM_X_SERVER_EXTRA_ARGS") != NULL) {
|
||||
+ g_debug ("forwarding GDM_X_SERVER_EXTRA_ARGS= %s", g_getenv("GDM_X_SERVER_EXTRA_ARGS"));
|
||||
+ gdm_session_worker_set_environment_variable (worker, "GDM_X_SERVER_EXTRA_ARGS",
|
||||
+ g_getenv("GDM_X_SERVER_EXTRA_ARGS"));
|
||||
+ }
|
||||
+
|
||||
gdm_session_worker_update_environment_from_passwd_info (worker,
|
||||
uid,
|
||||
gid,
|
|
@ -0,0 +1,40 @@
|
|||
diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c
|
||||
index 88fe96f..b1b140a 100644
|
||||
--- a/daemon/gdm-x-session.c
|
||||
+++ b/daemon/gdm-x-session.c
|
||||
@@ -664,18 +664,34 @@ spawn_session (State *state,
|
||||
state->session_command,
|
||||
NULL);
|
||||
} else {
|
||||
+ char const *session_wrapper;
|
||||
+ char *eff_session_command;
|
||||
int ret;
|
||||
char **argv;
|
||||
|
||||
- ret = g_shell_parse_argv (state->session_command,
|
||||
+ session_wrapper = g_getenv("GDM_X_SESSION_WRAPPER");
|
||||
+ if (session_wrapper != NULL) {
|
||||
+ char *quoted_wrapper = g_shell_quote(session_wrapper);
|
||||
+ eff_session_command = g_strjoin(" ", quoted_wrapper, state->session_command, NULL);
|
||||
+ g_free(quoted_wrapper);
|
||||
+ } else {
|
||||
+ eff_session_command = state->session_command;
|
||||
+ }
|
||||
+
|
||||
+ ret = g_shell_parse_argv (eff_session_command,
|
||||
NULL,
|
||||
&argv,
|
||||
&error);
|
||||
|
||||
+ if (session_wrapper != NULL) {
|
||||
+ g_free(eff_session_command);
|
||||
+ }
|
||||
+
|
||||
if (!ret) {
|
||||
g_debug ("could not parse session arguments: %s", error->message);
|
||||
goto out;
|
||||
}
|
||||
+
|
||||
subprocess = g_subprocess_launcher_spawnv (launcher,
|
||||
(const char * const *) argv,
|
||||
&error);
|
Loading…
Reference in a new issue