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; };
|
nvidia_libs32 = (nvidiaForKernel pkgs_i686.linuxPackages).override { libsOnly = true; kernel = null; };
|
||||||
|
|
||||||
enabled = nvidia_x11 != null;
|
enabled = nvidia_x11 != null;
|
||||||
|
|
||||||
|
cfg = config.hardware.nvidia;
|
||||||
|
optimusCfg = cfg.optimus_prime;
|
||||||
in
|
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 {
|
config = mkIf enabled {
|
||||||
assertions = [
|
assertions = [
|
||||||
@ -36,15 +100,61 @@ in
|
|||||||
assertion = config.services.xserver.displayManager.gdm.wayland;
|
assertion = config.services.xserver.displayManager.gdm.wayland;
|
||||||
message = "NVidia drivers don't support 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
|
# If Optimus/PRIME is enabled, we:
|
||||||
{ name = "nvidia"; modules = [ nvidia_x11.bin ]; libPath = [ nvidia_x11 ]; };
|
# - 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 {
|
environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
|
||||||
source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
|
source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
|
||||||
@ -62,6 +172,8 @@ in
|
|||||||
boot.kernelModules = [ "nvidia-uvm" ] ++
|
boot.kernelModules = [ "nvidia-uvm" ] ++
|
||||||
lib.optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
|
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.
|
# Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
|
||||||
services.udev.extraRules =
|
services.udev.extraRules =
|
||||||
|
@ -222,6 +222,17 @@ in
|
|||||||
description = "List of arguments for the X server.";
|
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 {
|
sessionCommands = mkOption {
|
||||||
type = types.lines;
|
type = types.lines;
|
||||||
default = "";
|
default = "";
|
||||||
|
@ -7,6 +7,13 @@ let
|
|||||||
cfg = config.services.xserver.displayManager;
|
cfg = config.services.xserver.displayManager;
|
||||||
gdm = pkgs.gnome3.gdm;
|
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
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -112,6 +119,11 @@ in
|
|||||||
GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions";
|
GDM_SESSIONS_DIR = "${cfg.session.desktops}/share/xsessions";
|
||||||
# Find the mouse
|
# Find the mouse
|
||||||
XCURSOR_PATH = "~/.icons:${pkgs.gnome3.adwaita-icon-theme}/share/icons";
|
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";
|
execCmd = "exec ${gdm}/bin/gdm";
|
||||||
};
|
};
|
||||||
|
@ -62,6 +62,12 @@ let
|
|||||||
${optionalString hasDefaultUserSession ''
|
${optionalString hasDefaultUserSession ''
|
||||||
user-session=${defaultSessionName}
|
user-session=${defaultSessionName}
|
||||||
''}
|
''}
|
||||||
|
${optionalString (dmcfg.setupCommands != "") ''
|
||||||
|
display-setup-script=${pkgs.writeScript "lightdm-display-setup" ''
|
||||||
|
#!${pkgs.bash}/bin/bash
|
||||||
|
${dmcfg.setupCommands}
|
||||||
|
''}
|
||||||
|
''}
|
||||||
${cfg.extraSeatDefaults}
|
${cfg.extraSeatDefaults}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ let
|
|||||||
Xsetup = pkgs.writeScript "Xsetup" ''
|
Xsetup = pkgs.writeScript "Xsetup" ''
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
${cfg.setupScript}
|
${cfg.setupScript}
|
||||||
|
${dmcfg.setupCommands}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
Xstop = pkgs.writeScript "Xstop" ''
|
Xstop = pkgs.writeScript "Xstop" ''
|
||||||
@ -137,7 +138,8 @@ in
|
|||||||
xrandr --auto
|
xrandr --auto
|
||||||
'';
|
'';
|
||||||
description = ''
|
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.";
|
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 {
|
xrandrHeads = mkOption {
|
||||||
default = [];
|
default = [];
|
||||||
example = [
|
example = [
|
||||||
@ -754,6 +760,7 @@ in
|
|||||||
Driver "${driver.driverName or driver.name}"
|
Driver "${driver.driverName or driver.name}"
|
||||||
${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
|
${if cfg.useGlamor then ''Option "AccelMethod" "glamor"'' else ""}
|
||||||
${cfg.deviceSection}
|
${cfg.deviceSection}
|
||||||
|
${driver.deviceSection or ""}
|
||||||
${xrandrDeviceSection}
|
${xrandrDeviceSection}
|
||||||
EndSection
|
EndSection
|
||||||
|
|
||||||
@ -765,6 +772,7 @@ in
|
|||||||
''}
|
''}
|
||||||
|
|
||||||
${cfg.screenSection}
|
${cfg.screenSection}
|
||||||
|
${driver.screenSection or ""}
|
||||||
|
|
||||||
${optionalString (cfg.defaultDepth != 0) ''
|
${optionalString (cfg.defaultDepth != 0) ''
|
||||||
DefaultDepth ${toString cfg.defaultDepth}
|
DefaultDepth ${toString cfg.defaultDepth}
|
||||||
@ -794,6 +802,8 @@ in
|
|||||||
'')}
|
'')}
|
||||||
|
|
||||||
${xrandrMonitorSections}
|
${xrandrMonitorSections}
|
||||||
|
|
||||||
|
${cfg.extraConfig}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
fonts.enableDefaultFonts = mkDefault true;
|
fonts.enableDefaultFonts = mkDefault true;
|
||||||
|
@ -37,13 +37,27 @@ stdenv.mkDerivation rec {
|
|||||||
|
|
||||||
# Disable Access Control because our X does not support FamilyServerInterpreted yet
|
# Disable Access Control because our X does not support FamilyServerInterpreted yet
|
||||||
patches = [
|
patches = [
|
||||||
|
# Change hardcoded paths to nix store paths.
|
||||||
(substituteAll {
|
(substituteAll {
|
||||||
src = ./fix-paths.patch;
|
src = ./fix-paths.patch;
|
||||||
inherit coreutils plymouth xwayland;
|
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
|
./sessions_dir.patch
|
||||||
|
|
||||||
|
# Allow specifying X server arguments with GDM_X_SERVER_EXTRA_ARGS.
|
||||||
./gdm-x-session_extra_args.patch
|
./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 = [
|
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 New Issue
Block a user