Как Linux рисует окна?
Вступление
Уже давно в Linux-сообществе ведутся горячие дискуссии о том, когда Wayland заменит Xorg. Однако далеко не все ясно понимают, что из себя представляют эти две технологии. В этой лекции мы постараемся выяснить:
- Что такое Xorg и почему он такой, какой он есть?
- Почему все ненавидят Xorg?
- Почему Wayland должен его заменить?
Неизбежно, изучив эту тему подробнее, мы узнаем:
- Как современные Linux-дистрибутивы рисуют графический интерфейс?
- Как в этом графическом интерфейсе появляются приложения?
- Почему графика в Linux далеко не такая производительная, как могла бы быть?
На нашем пути познания графики в Linux мы познаем:
- Искусство создания костылей
- Искусство создания плагинов для создания костылей
- Искусство сохранения обратной совместимости на протяжении почти половины века
- Искусство поддержки и рефакторинга кода, которому почти треть века
- Искусство выкидывание легаси-кода на протяжении более 15 лет
- А также искусство разногласий и лицензионных противоречий
Что такое X Window System?
X Window System - это оконная система, включающая себя стандартные инструменты и протоколы для построения графического интерфейса пользователя, а также использующаяся в Unix-like системах.
X Window System состоит из двух составляющих:
- Сервер - приложение, запущенное на целевом компьютере, который подключен к дисплею и устройствам ввода; X-сервер создаёт на целевом компьютере корневое окно, все остальные окна являются дочерними; при этом сам сервер не определяет, каким конкретно образом рисовать окна и оформление для них (например, как должны рисоваться заголовки у окон, кнопки сворачивания, разворачивания или закрытия); обычно этим занимаются оконные менеджеры - программы, работающие поверх X-сервера; но опять же, обязательства оконных менеджеров по тому, как и что они должны рисовать, нигде не прописаны, поэтому оформление у окон также могут рисовать и сами приложения (в таком случае их внешний вид определяется либо тулкитами, такими как GTK или QT, либо самим разработчиком).
- Клиент - приложение, которое взаимодействует с X-сервером; отображается в качестве окна внутри корневого окна, созданного сервером; как правило, это обычное прикладное приложение, по типу браузера, файлового менеджера, терминала, редактора кода и так далее; при этом клиент может выполняться на удалённой машине и передавать свой интерфейс по сети.
Таким образом X Window System основан на клиент-серверной архитектуре и обладает сетевой прозрачностью - неважно, где будет запущен клиент: на той же машине, что сервер, или на другой, будет ли иметь другая машина ту же архитектуру и аппаратное обеспечение или нет. Данная архитектура была очень удобной в том историческом контексте, в котором X создавался. Однако в будущем это сыграет с ней злую шутку.
Важной частью X Window System являются протоколы, по которому общаются клиент и сервер. Протокол можно сравнить с интерфейсами в ООП или рецептами, по которым мы готовим еду. Например, рецепт пирога - это протокол, а уже приготовленный пирог - это реализация. Так же протокол можно сравнить с языком, на котором общаются люди. Язык позволяет доносить информацию другому человеку так, чтобы он её понял. Так же и в X Window System. Клиенты и сервер работают по одному протоколу, что позволяет им понимать друг друга.
При этом что у клиента, что у сервера существует множество реализаций. В случае с клиентом это очевидно, у нас есть разные приложения, созданные для разных целей и рисующих разный интерфейс, и они реализуют протокол X, чтобы общаться с X-сервером, который позволит им отрисовать то, что им нужно, а так же обработать ввод пользователя, получая команды от X-сервера, так, как им нужно.
А вот примеры некоторых реализаций X-сервера:
- Xming - реализация X-сервера для Windows, позволяющая запускать совместимые с X приложения на этой системе
- Xquartz - то же самое, только для MacOS
- XFree86 - ранее самая популярная реализация X-сервера для Linux и других Unix-like систем
- XOrg - форк XFree86, сейчас считается канонической реализацией X-сервера
На момент создания X Window System подразумевалось, что X-сервер возьмёт на себя функции управления вводом и отрисовки интерфейса ОС и приложений на экран. Однако в следствие развития аппаратных технологий и повышения требований к интерфейсу, X-сервер перестал нормально справляться с этой задачей, в следствие чего многие функции из него начали выносить либо в ядро Linux, либо в тулкиты для создания приложений. Об этом будет рассказано далее.
Немного истории
Разработка протокола X началась в стенах MIT в 1984 году. X Window System разрабатывался совместными усилиями MIT, IBM и DEC, в рамках проекта Athena, чья цель была создание комьютерных технологий для обучения.
Изначальная идея - создание оконной системы, которая позволила бы объединить разношёрстные компьютерные системы университетов, а также которая позволила бы работать с собой как локально, так и удалённо.
За основу взяли появившийся стек протоколов TCP/IP. Новую оконную систему старались делать аппаратно независимой. Сначала ранние версии протокола лицензировались за деньги, после было решено сделать разработку открытой в рамках лицензии MIT. В 1987 году выходит финальная версия протокола - X11.
XFree86 и XOrg
Проект XFree86 возник в 1992 году из сервера X386 для IBM PC-совместимых компьютеров. Со временем XFree86 превратился из просто отдельно взятого порта X в ведущую и самую популярную реализацию системы и стал де-факто руководить разработкой X.
Разработка XFree86 велась очень вяло: патчи принимались медленно, изменения контроллировались узким кругом лиц, многие из которых были менеджерами, а не программистами. Кит Паккард, участвовавший в разработке, не был доволен текущим положением дел.
Последней каплей была смена лицензии на несовместимую с GPL. Кита Паккарда выкинули на мороз. Но он вместе с другими ключевыми разработчиками X в 2004 году основали X.Org Foundation, а также создали форк XFree86, который назвали XOrg. The Open Group передали ей управление доменом x.org. Под крылом X.Org Foundation в иксах был проведён серьёзный рефакторинг кода.
Раздутый монстр
Стоит отдать должно разработчикам X Window System, она остаётся на плаву уже на протяжении 40 лет. При этом умудряясь сохранять обратную совместимость с 11 версией, разработанной аж в 1987 году.
Однако из-за стремления сохранить обратную совместимость по итогу X стал крайне огромным и монструозным. В определённый момент времени X стал выполнять функции государства внутри государства, фактически беря большую часть функциональности операционной системы на себя. Однако, в попытках ухватиться за всё, что только можно, X не мог нормально выполнять ни одну из своих функций.
X-сервер имел возможность создания расширений - дополнительных модулей, позволяющие расширять базовую функциональность X Window System, добавляя в него те функции, которые не были предусмотрены базовым протоколом. Подобная система имело ряд преимуществ:
- Добавляя расширения вместо изменения базового протокола, мы не ломаем обратную совместимость
- Создавая расширения вместо изменения базового протокола, мы не нуждаемся в том, чтобы править запутанный legacy-код X-сервера
С появлением видеокарт, нескольких устройств ввода, систем на основе нескольких мониторов, появилась необходимость расширять функционал X11. Именно для этого начали использовать расширения.
OpenGL, GLX, DRI и немного рефакторинга
Sillicon Graphics - компания-разработчик графических станций, а также создатель OpenGL. Для своей операционной системы, которая использовалась на их графических станциях они решили выбрать оконную систему X11.
Задачей компании было подружить иксы с OpenGL для создания аппаратно ускоренного рабочего стола. Они создали расширение GLX, которое стало связующим звеном между OGL и X11.
Одновременно с этим RedHat и Sillicon Graphics создали архитектуру прямой отрисовки - DRI (Direct Rendering Infrastructure). По своей сути, это протокол, по которому X-сервер взаимодействует с драйверами видеокарты. Она позволила выводить изображение напрямую в обход X-сервера, а также обеспечить поддержку ускорение через GLX. Таким образом они добились повышения производительности графики.
Все эти изменения стали частью одного из самых значительных релизов XFree86 - версия 4.0. Помимо этого в нём появилось разделение на устройствозависимую часть (DDX - Device Dependent X) и на устройствонезависимую часть (DIX - Device Independent X). Данное разделение стало необходимым, так как разработчикам аппаратного обеспечения было крайне неудобно каждый раз добавлять в X-сервер всё новые и новые драйверы. Теперь часть, которая отвечает за отрисовку рабочего стола, 2D-ускорение, работу с DRI и OpenGL вынесли в DIX. По итогу эти драйверы стали звеном между X-сервером и Mesa.
Вынести всё из иксов, как цель жизни
Раньше иксы:
- Сами переключали видеорежимы
- Сами занимались 2D и 3D-ускорением через DRI
- Сами управляли ресурсами GPU, забирая эксклюзивный доступ к ним и используя root-права
- Управляли устройствами ввода
- Даже имели в себе шрифтовой движок и сервер печати
Видеорежимы иксы переключали плохо. При переключении в виртуальную консоль по нажатию Ctrl+Alt+F1 иксам приходилось резко сбрасывать управление ядру, переключаться на кадровый буфер ядра Linux (Linux Frame Buffer). При переключении же обратно в графический сеанс необходимо было проделывать это всё в обратном направлении. Это было крайне накладно и экран во время этих манипуляций на секунду покрывался артефактами. Порой возврат в нормальный рабочий режим вовсе ломался. Приходилось либо перезапускать иксы, либо вовсе всю систему. Для исправления этого управление видеорежимами решили вынести из иксов в ядро Linux. Для этого создали KMS - Kernel Mode Settings. Это облегчило иксы и повысило производительность графики.
X-сервер традиционно имел эксклюзивный доступ к видеокарте и работал через кадровый буфер Linux. Это требовало root-прав и приводила к разного рода неприятным последствиям, когда два или более приложения пытались получить доступ к графическому ускорителю, урвать себе кусок видеопамяти и так далее. Это работало бы хорошо, если бы X-сервер был единственной программой, которой необходимо получить все ресурсы GPU, что приводило к состоянию гонки. Для исправления этой проблемы была создана подсистема ядра Linux - DRM (Direct Rendering Manager). Она брала все ресурсы видеокарты себе и делила их между программами. Теперь пользователь может пользоваться аппаратно-ускоренным рабочим столом, смотря видео в плеере без единого разрыва и рендеря сцену в Блендере. Также DRM предоставила API для программ и библиотек. Также отпала нужда запускать X-сервер от имени суперпользователя.
Обработка устройств ввода так же была вынесена из X-сервера в ядро Linux. Теперь этим занимается подсистема evdev. Поддержку горячего подключения устройств тоже вынесли, этим занимается подсистема ядра udev.
Шрифтами по большей мере стали заведовать графические тулкиты, наподобие QT или GTK. Впрочем, шрифтовой сервер не был удалён из иксов для сохранения обратной совместимости.
Красиво и без разрывов
Наверняка вы сталкивались с проблемой тиринга (разрыва экрана), когда играли в игры на компьютере без включённой вертикальной синхронизации. Та же проблема присутствовала и у X11. Помимо всего прочего у X11 не было возможности рисовать всевозможные красивые анимации и сложные эффекты.
Всему виной метод, как X11 рисовал окна. А рисовал он их сразу в экранный framebuffer. Этот метод хорошо работал, когда компьютеры ещё не были достаточно мощными. Однако со временем у компьютеров стало больше памяти и теперь можно использовать дополнительный буфер.
Делается это при помощи механизма перенаправления. Когда окно перенаправляется, X-сервер создаёт отдельный буфер (pixmap) для каждого окна. Все графические операции, которые раньше выполнялись непосредственно в кадре памяти экрана, теперь выполняются в этих буферах.
Композитный оконный менеджер (compositing manager) — это отдельное приложение, которое отвечает за компоновку (compositing) всех оконных буферов в конечный кадр памяти экрана. Композитный менеджер получает доступ к содержимому всех буферов, комбинирует их с учётом всех визуальных эффектов (таких как прозрачность и тени) и выводит результат на экран.
Помимо преимуществ, связанных с эффектами, теперь появляется возможность делать более сложное взаимодействие с окнами
Для обеспечения композитинга было создано расширение Composite. Оно позволяет композиционному менеджеру окон создать Composite Overlay Window (сокращённо COW, окно композиционного слоя). После этого композитор назначается владельцем окна COW и может проводить его отрисовку.
Когда вы запускаете Compiz или GNOME Shell, эти приложения используют OpenGL для отображения перенаправленных окон на экране. X-сервер даёт им пользоваться содержимым окон с помощью GL-расширения Texture from Pixmap («текстура из пиксельной карты») или TFP. Это расширение позволяет OpenGL-приложению использоваться пиксельные карты X11 так, как если бы это были нативные текстуры OpenGL. Композиционные менеджеры окон, в принципе, могут не использовать TFP или OpenGL.
А что стало с сетевой прозрачностью?
В следствие того, что многие приложения начали себя рисовать при помощи аппаратного ускорения, теперь по сети по сути передаются готовые битмапы, что создают большое количество трафика. Xorg превратился в плохой аналог VNC. Дополнительно проблему усугубляет DBus, который не умеет работать по сети.
Ещё немного про проблемы XOrg
Из-за того, что композитинг по сути прикручен сбоку, это сильно сажает производительность. В некоторых оконных менеджерах даже есть настройка, позволяющая отключать композитинг в полноэкранном режиме.
Иксы плохо работают с несколькими мониторами. Особенно если у них разный DPI. Если вывод изображения с разным разрешением ещё можно сделать, то разную плотность пикселей уже нельзя выставить. Иначе говоря, на одном мониторе будет всё нормально, на другом - мыло.
Иксы плохо работают в многопоточном режиме. Это приводит к тому, что, например, при выводе сложного изображения или при наложении на него сложных фильтров рабочий стол может потенциально зависнуть или начать сильно лагать.
Xorg имеет монопольный захват устройств ввода и беспрепятственный доступ к корневому окну. Это приводит к различиным багам, вот пример некоторых из них:
- Несрабатывание хранителя экрана, если открыто меню приложений
- Экран входа в систему блокирует кнопки управления плеером
- При открытом меню приложений нельзя сделать скриншот
Также это приводит также к проблемам безопасности. Если злоумышленник получит доступ к вашему рабочему столу он сможет без труда прочитать всё содержимое экрана, а также захватывать клавиатурный ввод, что может привести к похищению паролей.
И ещё один забавный факт. Для X11 существует расширение XFixes, который буквально упрощает создание костылей… При помощи него, например, сделано скрытие курсора при просмотра видео, а также смена стиля курсора, когда мы направляем его на разные элементы интерефейса.
А чё делает X-сервер?
Предлагаю ещё раз взглянуть на архитектуру X11, чтобы понять, а что всё же делает X-сервер. Схема и пункты взяты с сайта Wayland.
- Ядро получает событие от устройства ввода и отправляет его в X через драйвер ввода evdev. Ядро выполняет всю тяжелую работу, управляя устройством и переводя различные протоколы событий, специфичные для устройства, в стандарт входных событий linux evdev.
- X-сервер определяет, какое окно затрагивает событие, и отправляет его клиентам, которые выбрали для данного события это окно. На самом деле X-сервер не знает, как правильно это сделать, поскольку расположение окна на экране контролируется композитором и может быть преобразовано различными способами, которые X-сервер не понимает (уменьшение масштаба, поворот, шатание и т. д.).
- Клиент смотрит на событие и решает, что делать. Часто пользовательский интерфейс должен измениться в ответ на событие - возможно, был нажат флажок или указатель попал на кнопку, которую нужно выделить. Таким образом, клиент посылает запрос на рендеринг обратно на X-сервер.
- Когда X-сервер получает запрос на рендеринг, он отправляет его драйверу, чтобы тот запрограммировал оборудование на выполнение рендеринга. X-сервер также рассчитывает граничную область рендеринга и отправляет ее в композитор как damage event.
- Damage event сообщает композитору, что в окне что-то изменилось и что он должен перекомпоновать ту часть экрана, где это окно видно. Композитор отвечает за рендеринг содержимого всего экрана на основе его сценографа и содержимого X-окон. Однако для этого он должен пройти через X-сервер.
- X-сервер получает запросы на рендеринг от композитора и либо копирует задний буфер композитора в передний буфер, либо выполняет перелистывание страниц. В общем случае X-сервер должен выполнить этот шаг, чтобы учесть перекрывающиеся окна, которые могут потребовать обрезки, и определить, может ли он перевернуть страницу. Однако для композитора, который всегда работает в полноэкранном режиме, это еще одно ненужное переключение контекста.
Как уже говорилось выше, с таким подходом есть несколько проблем. У X-сервера нет информации, чтобы решить, какое окно должно получить событие, и он не может преобразовать координаты экрана в локальные координаты окна. И хотя X передал ответственность за окончательную закраску экрана менеджеру композитинга, X по-прежнему контролирует передний буфер и настройку режимов. Большинство сложностей, которые раньше решал X-сервер, теперь доступны в ядре или в самостоятельных библиотеках (KMS, evdev, mesa, fontconfig, freetype, cairo, Qt и т. д.). В общем, X-сервер теперь - это просто посредник, который создает дополнительный шаг между приложениями и композитором и дополнительный шаг между композитором и оборудованием. Вопрос, зачем он нужен?
А давайте выкинем X-сервер?
“А давайте!” - сказал Кристин Хогсберг и сделал Wayland.
В Wayland композитор является сервером отображения. Мы передаем композитору управление KMS и evdev. Протокол wayland позволяет композитору отправлять входные события непосредственно клиентам и позволяет клиенту отправлять damage-события непосредственно композитору.
- Ядро получает событие и отправляет его в композитор. Это похоже на случай X, что замечательно, поскольку мы получаем возможность повторно использовать все драйверы ввода в ядре.
- Композитор просматривает свою сценарную схему, чтобы определить, какое окно должно получить событие. Граф сцены соответствует тому, что находится на экране, и композитор понимает преобразования, которые он мог применить к элементам в графе сцены. Таким образом, композитор может выбрать нужное окно и преобразовать экранные координаты в локальные координаты окна, применив обратные преобразования. Типы преобразований, которые могут быть применены к окну, ограничены только тем, что может сделать композитор, если он может вычислить обратное преобразование для входных событий.
- Как и в случае с X, когда клиент получает событие, он обновляет пользовательский интерфейс в ответ. Но в случае с Wayland рендеринг происходит в клиенте, а клиент просто посылает запрос композитору, чтобы указать область, которая была обновлена.
- Композитор собирает damage-запросы от своих клиентов и затем перекомпонует экран. Затем композитор может напрямую выдать ioctl для планирования перелистывания страниц с помощью KMS.