Pull to refresh

Графическая среда Linux без единого разрыва

Reading time4 min
Views21K

TL;DR — Если ваше графическое окружение Linux во время просмотра видео, сеанса игры или прокрутки интерактивной веб страницы не успевает вовремя обновлять картинку целиком, то тогда для вас имеет смысл установить последнюю стабильную версию ядра ≥ 4.10.


Давным давно, то есть несколько лет назад каждая реализация протокола X11 предполагала смену режима видео напрямую, поперек батьки кернела. Затем появился KMS (kernel mode setting) и эта важная функция перешла к ядру. Но остались некоторые шероховатости. Атомарная смена режима является дальнейшим улучшением механизма KMS.


Для чего нужны атомарные операции KMS? Главным образом для того, чтобы избежать вот таких моментов.





Атомарная смена режима видео


DRM драйвер с поддержкой атомарной смены режима, a․ k․ a․ atomic mode setting имеет полезное свойство, которое заключается в том, что изменения видео режима проходят полную проверку прежде, чем вступят в силу. Это делается с целью обеспечить их корректное исполнение в драйверах и на дисплее с тем, чтобы избавить пользователей от мерцания, тиринга и прочих артефактов изображения. Скорость исполнения при этом также повышается. Звучит неплохо, а как это работает?


  • Framebuffer — Видео память, однако в терминологии KMS это скорее пул источников памяти видео — объектов GEM, для которого заданы такие характеристики как активная зона памяти, или то, что будет изображено, а также формат данных, длина шага.

Framebuffer





DRM/KMS Components:  Framebuffer
struct drm_framebuffer {
[...]
    unsigned int pitches[4];
    unsigned int offsets[4];
    unsigned int width;
    unsigned int height;
    int flags;
    [...]

  • CRTC — Аббревиатура расшифровывается как Cathode Ray Tube Controller, CRT Controller. Мало кто в нынешнее время использует мониторы на электронно-лучевых трубках, однако историческое название сохранилось с виде абстракции железа, которое считывает байты с памяти видео-карты и выдает пиксели на шину данных.
  • Encoder — Интерфейс между разнородными источниками видео, читает с CRTC и передает в соединительный разъем, a․ k․ a․ Connector. Один CRTC может иметь несколько кодеров.
  • Connector — Это может быть представлением для соединительного разъема монитора или же самим монитором в случае встроенных устройств с подключенных внешним экраном.
  • Planes — Слой или план изображения на CRTC.




Для этого надо понять как изменяется видео режим без этого нововведения. Рассмотрим обычный сценарий, в котором пользователь смотрит видео в окне браузера или плеера, не в полный экран, используя аппаратное ускорение. Видео образует передний план, окно и декорации браузера или плеера, это второй — задний план.


  1. Драйверу видео передается список различных параметров: кадровый буфер, CRTC, экраны, режим.
  2. Пользователь перемещает окно плеера.
  3. Для этого нужно подгрузить новую страницу, a․ k․ a․ page flip, например.
  4. Если новая страница не синхронизирована между передним и задним планом, видео сместится относительно своего окна.

struct drm_mode_crtc_page_flip {
     __u32 crtc_id;
     __u32 fb_id;
     __u32 flags;
     __u32 reserved;
     __u64 user_data;
};

Механизма, обеспечивающего синхронизацию на предпоследнем шаге нет, отсутствовал ioctl(), который выполнял бы всю работу. Например, только основной план имел механизм неблокирующих обновлений критичных с точным завершением событий. А для того, чтобы обновления произошли в нескольких слоях, требовалось куча системных вызовов из пользовательского пространства в надежде на то, что они завершатся синхронно. В то же время атомарные операции KMS имеют встроенную защиту от этого. Вместо трех разных ioctl(), все изменения проходят в одном единственном ioctl().


Вообще-то проблему отсутствия синхронизации между активной зоной и задним планом во время просмотра видео решалась компоновкой всего с помощью GL, так как последний умеет обновлять кадры синхронно с VBlank. Все бы хорошо, только вот для мобильных устройств это не приемлемо из за высоких требования к памяти и питанию со стороны GL компоновщика.


Работы над «атомным» проектом началась в 2015-м с патчей Дейва Эйрли (Dave Airlie), затрагивающих интеловские i915 и еще несколько драйверов, плюс новое атомарное API.


В настоящий момент атомарный процесс обновлений происходит следующим образом.


  1. Все изменения передаются ядру одним списком свойств (в середине картинки Properties).
  2. Ядро генерирует состояние устройства (справа на картинке State).
  3. atomic_check() проверяет валидность всех элементов списка свойств. Если есть ошибка ioctl() вернет уведомление об ошибка и отменит обновления.
  4. atomic_commit() также в соответствии с названием вводит изменения в действие, если на предыдущем шаге atomic_check() завершился без ошибок.




Структура атомарной KMS определена в файле /usr/include/uapi/drm/drm_mode.h.


#define DRM_MODE_PAGE_FLIP_EVENT 0x01
#define DRM_MODE_PAGE_FLIP_ASYNC 0x02
#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
#define DRM_MODE_ATOMIC_NONBLOCK 0x0200
#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
struct drm_mode_atomic {
    __u32 flags;
    __u32 count_objs;
    __u64 objs_ptr;
    __u64 count_props_ptr;
    __u64 props_ptr;
    __u64 prop_values_ptr;
    __u64 reserved;
    __u64 user_data;
};

Для открытых драйверов Nouveau от Nvidea атомарный KMS включен по умолчанию начиная с версии Linux 4.10, для драйверов Intel — начиная с 4.12. Дальше — больше!


Использованные материалы


Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+20
Comments14

Articles