воскресенье, 30 октября 2016 г.

Старая видеокарта + Linux + монитор с разрешением 2560x1440

Очередной пост из тех, что не интересны никому, кроме отчаявшихся искателей решения своей проблемы в сети по ключевым словам.

Видеокарты GeForce GTS 450 были выпущены в 2010 году. Уже тогда существовали мониторы с разрешением 2560×1440 и 2560×1660, которые видеокарта, номинально, поддерживала. В 2016 году появились на свет мониторы DELL U2717D с разрешением 2560×1440. Одному из братьев не повезло: судьба забросила его на компьютер с GTS 450 с линуксом, который упорно выставлял 1920×1080, искренне считая, что ни монитор, ни видеокарта не способны на большее.

Видеокарта

Цифровое изображение, как и аналоговое, поступает на монитор в виде потока кадров с некоторой частотой. Обычная частота регенерации — 60 Гц.

Примечание: на ЭЛТ-мониторах частота регенерации совпадала с частотой мерцания изображения на мониторе; при частоте ниже 80 Гц у пользователей болели глаза. В ЖК-мониторах изображение не мерцает, но тем не менее обновляется с фиксированной частотой.

GTS 450 неспособна выдавать кадры размером 2560×1440 60 раз в секунду. В принципе, не любая частота возможна, даже в допустимых рамках. Например, частоту 30 Гц данная видеокарта выдать не может, а 33 Гц — может. Это не написано ни в какой документации и выясняется опытным путём.

Если бы мы жили в справедливой вселенной, то при подключении нового монитора драйвер видеокарты автоматически подобрал бы оптимальные для этого монитора параметры из тех, которые поддерживает видеокарта, и монитор заиграл бы всеми пикселями. В нашей вселенной ничего этого не происходит и всё согласование приходится проводить вручную.

Отметим, что линуксовые пользователи видеокарт Nvidia наделены выбором, какой драйвер использовать: проприетарный (nvidia) или свободный (nouveau). Ещё одна опция заключена в виде кабеля (VGA/DVI/HDMI). Так вот, со свободным драйвером ничего не выйдет. С DVI-кабелем тоже (включая Dual-link). По аналоговому VGA-кабелю карта не может передать такой сигнал (это, в отличие от всего остального, упомянуто в спецификации). Единственная работающая комбинация: проприетарный драйвер + HDMI.

Настройка видеосигнала имеет больше параметров, чем ширина, высота и частота обновления. Не вдаваясь в детали, скажем, что это 9 чисел + набор необязательных флагов. Общеупотребимый синтаксис задания этих настроек называется Modeline. В случае, когда с видеокарты требуется подать сигнал с параметрами, которые драйвер не считает нужным знать (это как раз наш случай), можно сообщить драйверу Modeline этого сигнала.

Но как подобрать Modeline, зная только искомые ширину, высоту и частоту? Для унификации Modeline в видеокартах разных производителей есть (точнее, был) стандарт VESA GTF (Generalized Timing Formula). Воплощением этого стандарта на практике является одноимённая утилита командной строки:

$ gtf 2560 1440 33

  # 2560x1440 @ 33.00 Hz (GTF) hsync: 48.44 kHz; pclk: 162.77 MHz
  Modeline "2560x1440_33.00"  162.77  2560 2688 2960 3360  1440 1441 1444 1468  -HSync +Vsync

Как теперь сообщить Modeline драйверу? Многие делают это командой xrandr --newmode (например, Линус Торвальдс), но в проприетарном драйвере nvidia эта команда не поддерживается. Она поддерживается в nouveau, но не даёт результата в нашем случае. Кстати, стандарт GTF существовал до 2002 года, после чего был заменён на Coordinated Video Timings, реализованный утилитой cvt. Эта утилита выдаёт немного другой Modeline, который, как вы уже догадались, не работает.

Но мы отвлеклись. В проприетарном драйвере nvidia есть возможность задать Modeline путём добавления строки в секцию «Monitor» файла xorg.conf. Да, того самого, который устарел и не включается в дистрибутивы примерно с 2010 года. Впрочем, настройки через xorg.conf действуют и в современных версиях Xorg — если пользователь создаст этот файл.

Небольшая удача заключается в том, что графическая утилита nvidia-settings, входящая в состав драйвера nvidia, поможет нам создать xorg.conf, соответствующий оному драйверу и настройкам пользователя. Опция задания Modeline в утилите отсутствует (иначе всё было бы слишком просто, не так ли). Остаётся вписать желанные параметры вручную в уже сгенерированный утилитой файл. И обнаружить, что драйвер не хочет их выставлять.

Монитор

Драйвер отказывается передавать монитору сигнал, который тот не может принять (по идее, это способствует сохранению монитора от перегорания, ну и вообще, к чему такие трюки). Мониторы, выпускаемые c 2000-х годов (вспомните слово plug-n-play), рапортуют видеокарте о своих способностях посредством передачи EDID (Extended Display Identification Data). От монитора Dell U2717D драйвер получает некорректный EDID; виновен в этом либо монитор, либо драйвер, что, в сущности, неважно.

Отключить проверку EDID придётся вновь через опции драйвера в xorg.conf. Перечень опций приведён в конце поста. После перезапуска Xorg на мониторе устанавливается желанное разрешение — но проблемы на этом не заканчиваются.

Софт

На мониторе непривычно мелкий текст. Почему? Система рассчитывает физический размер шрифта по количеству точек на дюйм (DPI), переданного монитором в EDID. Но мы отключили EDID, да его и не было. Поэтому истинный DPI остался системе неведом, и она использует наиболее распространённое значение 96 DPI. На самом деле, в указанном мониторе DPI равно 109.

Примечание: DPI напрямую не указан в спецификации монитора. Его можно рассчитать несколькими способами по другим показателям. В спецификации ошибочно указана ширина активной области (569.736 вместо 596.736 мм), поэтому при расчёте по ширине у доверчивых пользователей получится 114 вместо 109.

Установка DPI, как и всё предыдущее, ложится на плечи пользователя. В xorg.conf есть опция задания DPI. Кажется, она не работает, но лучше оставить, вдруг какие-то программы обратят внимание на неё. То, что работает явно и сразу — это задание DPI через xrandr:

xrandr --dpi 109
или
xrandr --fbmm 597x336
(правильные размеры рабочей области в миллиметрах).

Автозапуск этой команды имеет смысл делать после загрузки Xorg; например, в файле .xinitrc (более того, автору неизвестны никакие другие файлы, в которых добавление этой команды даёт ожидаемый результат). Итак, на мониторе теперь шрифты привычного размера. Везде, кроме браузера Google Chrome.

Проблема этого браузера в том, что он игнорирует системные настройки DPI. Собственной настройки DPI он также не имеет, считая DPI равным 96, в любую погоду.

Перекос Chrome в сторону меньшего DPI может исправить увеличенный дефолтный параметр зума (недефолтный зум регулируется кнопками Ctrl_+ и Ctrl_-, а дефолтный включается по Ctrl_0). Вычисляем требуемое значение: 109/96 ≈ 114%, идём в настройки браузера, находим там опцию «Page zoom» и обнаруживаем, что 114% в ней не поставить. В меню есть 110% или 125%. Но разве нас это остановит после всех мучений? Вызываем Inspect для меню, добавляем в него нужный пункт (или подменяем имеющийся) и выбираем. При перезапуске Chrome значение 114% исчезнет из меню зума — но сам зум останется на 114% и будет компенсировать неверное значение DPI.

Приложение: xorg.conf

Настройки собраны с форумов и могут быть избыточны; но зачем тратить время на выявление лишних, если конструкция в целом работает.

Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0" 0 0
    InputDevice    "Keyboard0" "CoreKeyboard"
    InputDevice    "Mouse0" "CorePointer"
    Option         "Xinerama" "0"
EndSection

Section "Files"
EndSection

Section "InputDevice"
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "auto"
    Option         "Device" "/dev/psaux"
    Option         "Emulate3Buttons" "no"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection

Section "Monitor"
    Identifier     "Monitor0"
    VendorName     "Unknown"
    ModelName      "DELL U2717D"
    HorizSync       30.0 - 88.0
    VertRefresh     50.0 - 75.0
    Option         "DPMS"
    Option         "DPI" "109 x 109"
    Modeline "2560x1440"  162.77  2560 2688 2960 3360  1440 1441 1444 1468  -HSync +Vsync
EndSection

Section "Device"
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "GeForce GTS 450"
    Option         "ModeValidation" "NoEdidModes, AllowNonEdidModes, AllowNon60HzDFPModes, NoMaxPclkCheck, NoEdidMaxPclkCheck, NoHorizSyncCheck, NoVertRefreshCheck, NoEdidDFPMaxSizeCheck, NoPredefinedModes"
EndSection

Section "Screen"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    Option         "Stereo" "0"
    Option         "UseEDID" "False"
    Option         "UseEDIDDPI" "False"
    Option         "UseEDIDFreqs" "False"
    Option         "ExactModeTimingsDVI" "True"
    Option         "metamodes" "2560x1440 +0+0"
    Option         "SLI" "Off"
    Option         "MultiGPU" "Off"
    Option         "BaseMosaic" "off"
    Option         "DPI" "109 x 109"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection