Граф сцен Qt Quick
Граф сцен в Qt Quick
Qt Quick 2 использует специализированный граф сцен и ряд адаптаций, по умолчанию использующих OpenGL ES 2.0 или OpenGL 2.0 для отрисовки. Использование графа сцен для графики вместо традиционных императивных систем рисования (QPainter и аналогичных) означает, что сцену, подлежащую отрисовке, можно сохранить между кадрами, а весь набор примитивов для отрисовки известен до начала отрисовки. Это открывает возможности для ряда оптимизаций, таких как пакетная отрисовка для минимизации изменений состояния и отбрасывание скрытых примитивов.
Например, предположим, что пользовательский интерфейс содержит список из десяти элементов, где каждый элемент имеет цвет фона, значок и текст. Используя традиционные методы рисования, это приведет к 30 вызовам отрисовки и аналогичному количеству изменений состояния. Граф сцен, с другой стороны, может переупорядочить примитивы для отрисовки таким образом, чтобы все фоны были нарисованы одним вызовом, затем все значки, затем весь текст, сократив общее количество вызовов отрисовки всего до 3. Такая пакетная обработка и сокращение изменений состояния могут значительно улучшить производительность на некоторых устройствах.
Граф сцен тесно связан с Qt Quick 2.0 и не может использоваться автономно. Граф сцен управляется и отрисовывается классом QQuickWindow, а пользовательские типы элементов могут добавлять свои графические примитивы в граф сцен с помощью вызова QQuickItem::updatePaintNode().
Граф сцен представляет собой графическое представление сцены элементов, независимой структуры, содержащей достаточную информацию для отрисовки всех элементов. После его настройки им можно манипулировать и отрисовывать независимо от состояния элементов. На многих платформах граф сцен даже будет отрисовываться на отдельной потоке отрисовки, пока поток GUI готовит состояние следующего кадра.
Примечание: Большая часть информации на этой странице специфична для адаптации OpenGL графа сцен Qt Quick. Более подробную информацию о различных адаптациях графа сцен см. в разделе Адаптации графа сцен.
Структура графа сцен Qt Quick
Граф сцен состоит из ряда предопределенных типов узлов, каждый из которых служит определённой цели. Хотя мы называем это графом сцен, более точное определение — дерево узлов. Дерево создаётся из типов QQuickItem в QML-сцене, а затем внутренне обрабатывается рендерером, который отрисовывает сцену. Узлы сами по себе не содержат активный код отрисовки, ни виртуальную функцию paint().
Несмотря на то, что дерево узлов в основном создаётся внутренне существующими типами Qt Quick QML, пользователи также могут добавлять полные поддеревья со своим содержимым, включая поддеревья, представляющие 3D-модели.
Узлы
Наиболее важным узлом для пользователей является QSGGeometryNode. Он используется для определения пользовательской графики путём определения геометрии и материала. Геометрия определяется с помощью QSGGeometry и описывает форму или сетку графического примитива. Это может быть линия, прямоугольник, многоугольник, множество разрозненных прямоугольников или сложная 3D-сетка. Материал определяет, как заполняются пиксели в этой форме.
Узел может иметь любое количество дочерних элементов, и геометрические узлы будут отрисовываться так, чтобы они появлялись в порядке дочерних элементов, а родители находились за своими дочерними элементами.
Примечание: Это ничего не говорит о фактическом порядке отрисовки в рендерере. Гарантируется только визуальный вывод.
Доступные узлы:
Реализует функцию обрезки в графе сцен |
|
Используется для всего отрисовываемого содержимого в графе сцен |
|
Базовый класс для всех узлов в графе сцен |
|
Используется для изменения непрозрачности узлов |
|
Реализует преобразования в графе сцен |
Пользовательские узлы добавляются в граф сцен путём наследования от QQuickItem::updatePaintNode() и установки флага QQuickItem::ItemHasContents.
Предупреждение: Ключевым моментом является то, что операции OpenGL и взаимодействие с графом сцен происходят исключительно в потоке отрисовки, прежде всего во время вызова updatePaintNode(). Правило — использовать только классы с префиксом "QSG" внутри функции QQuickItem::updatePaintNode().
Более подробную информацию см. в разделе Граф сцен — настройка геометрии.
Предварительная обработка
Узлы имеют виртуальную функцию QSGNode::preprocess(), которая вызывается перед отрисовкой графа сцен. Подклассы узлов могут установить флаг QSGNode::UsePreprocess и переопределить функцию QSGNode::preprocess(), чтобы выполнить окончательную подготовку своего узла. Например, разделение кривой Безье на правильный уровень детализации для текущего масштаба или обновление участка текстуры.
Владение узлами
Владение узлами выполняется явно создателем или графом сцен путём установки флага QSGNode::OwnedByParent. Назначение владения графу сцен часто предпочтительнее, так как оно упрощает очистку, когда граф сцен существует вне потока GUI.
Материалы
Материал описывает, как заполняется внутренняя часть геометрии в QSGGeometryNode. Он инкапсулирует программу OpenGL шейдера и обеспечивает достаточную гибкость в том, что можно достичь, хотя большинство элементов Qt Quick сами используют только очень простые материалы, такие как заливка твёрдым цветом и текстурой.
Пользователи, которые хотят просто применить пользовательскую затенение к типу QML Item, могут сделать это непосредственно в QML, используя тип ShaderEffect.
Ниже приведён полный список классов материалов:
Удобный способ отрисовки геометрии твёрдым цветом в графе сцен |
|
Инкапсулирует состояние отрисовки для программы шейдера |
|
Представляет программу OpenGL шейдера в рендерере |
|
Используется в качестве уникального маркера типа в сочетании с QSGMaterial |
|
Удобный способ отрисовки текстурированной геометрии в графе сцен |
|
Класс, сгенерированный шаблоном, используемый для хранения состояния, используемого с QSGSimpleMateralShader |
|
Удобный способ создания пользовательских материалов на основе OpenGL для графа сцен |
|
Удобный способ отрисовки текстурированной геометрии в графе сцен |
|
Удобный способ отрисовки геометрии с цветом на вершине в графе сцен |
Более подробную информацию см. в разделе Граф сцен — простой материал
Узлы-утилиты
API графа сцен очень низкоуровневый и ориентирован на производительность, а не на удобство. Написание пользовательских геометрий и материалов с нуля, даже самых простых, требует значительного количества кода. По этой причине API включает несколько вспомогательных классов, чтобы сделать наиболее распространённые пользовательские узлы легкодоступными.
- QSGSimpleRectNode — подкласс QSGGeometryNode, который определяет прямоугольную геометрию с твёрдым цветом.
- QSGSimpleTextureNode — подкласс QSGGeometryNode, который определяет прямоугольную геометрию с текстурным материалом.
Граф сцен и отрисовка
Отрисовка графа сцен происходит во внутреннем классе QQuickWindow, и нет публичного API для доступа к нему. Однако есть несколько мест в процессе отрисовки, где пользователь может прикрепить код приложения. Это можно использовать для добавления пользовательского содержимого графа сцен или отрисовки необработанного содержимого OpenGL. Точки интеграции определяются циклом рендеринга.
Подробное описание работы рендерера графа сцен для OpenGL см. в разделе Qt Quick рендерер графа сцен OpenGL.
Доступны три варианта цикла рендеринга: basic, windows, и threaded. Из них basic и windows однопоточные, а threaded выполняет отрисовку графа сцен в отдельном потоке. Qt пытается выбрать подходящий цикл в зависимости от платформы и, возможно, графических драйверов. Если это не удовлетворяет, или для целей тестирования, переменная среды QSG_RENDER_LOOP может быть использована для принудительного использования заданного цикла. Чтобы проверить, какой цикл рендеринга используется, включите qt.scenegraph.general категорию регистрации.
Примечание: Циклы рендеринга threaded и windows полагаются на реализацию OpenGL для регулирования частоты кадров путём запроса интервала переключения 1. Некоторые графические драйверы позволяют пользователям переопределить этот параметр и отключить его, игнорируя запрос Qt. Без блокировки в операции обмена буферами (или в другом месте), цикл рендеринга будет запускать анимации слишком быстро и перегружать процессор на 100%. Если известно, что система не может обеспечить регулирование частоты кадров на основе vsync, используйте цикл рендеринга basic вместо этого, установив QSG_RENDER_LOOP=basic в среде.
Потоковый цикл рендеринга («потоковый»)
Во многих конфигурациях отрисовка графа сцены происходит в отдельной потоковой нити отрисовки. Это делается для повышения параллельности многоядерных процессоров и более эффективного использования времени простоя, такого как ожидание вызова буфера обмена, блокирующего выполнение. Это обеспечивает существенные улучшения производительности, но накладывает определённые ограничения на то, где и когда может происходить взаимодействие с графом сцены.
Ниже приведён упрощённый обзор того, как кадр создаётся в потоковой нити отрисовки.
- Происходит изменение в QML-сцене, что вызывает вызов
QQuickItem::update(). Это может быть результатом, например, анимации или пользовательского ввода. В поток отрисовки публикуется событие для инициирования нового кадра. - Поток отрисовки готовится к отрисовке нового кадра, делает контекст OpenGL текущим и начинает блокировку потока GUI.
- Пока поток отрисовки подготавливает новый кадр, поток GUI вызывает QQuickItem::updatePolish() для завершения обработки элементов перед их отрисовкой.
- Поток GUI заблокирован.
- Выдаётся сигнал QQuickWindow::beforeSynchronizing(). Приложения могут установить прямые соединения (используя Qt::DirectConnection) с этим сигналом для выполнения необходимой подготовки перед вызовами QQuickItem::updatePaintNode().
- Синхронизация состояния QML в граф сцены. Это делается путём вызова функции QQuickItem::updatePaintNode() для всех элементов, которые изменились с предыдущего кадра. Это единственный момент, когда QML-элементы и узлы в графе сцены взаимодействуют.
- Блокировка потока GUI снимается.
- Граф сцены отрисовывается:
- Выдаётся сигнал QQuickWindow::beforeRendering(). Приложения могут установить прямые соединения (используя Qt::DirectConnection) с этим сигналом для использования пользовательских вызовов OpenGL, которые будут визуально накладываться под QML-сцену.
- Элементы, у которых задан флаг QSGNode::UsePreprocess, будут иметь вызванную функцию QSGNode::preprocess().
- Рендерер обрабатывает узлы и вызывает функции OpenGL.
- Выдаётся сигнал QQuickWindow::afterRendering(). Приложения могут установить прямые соединения (используя Qt::DirectConnection) с этим сигналом для использования пользовательских вызовов OpenGL, которые будут визуально накладываться поверх QML-сцены.
- Отображённый кадр меняется, и выдаётся QQuickWindow::frameSwapped().
- Пока поток отрисовки выполняет отрисовку, поток GUI может выполнять продвижение анимаций, обработку событий и т. д.
Потоковый рендерер используется по умолчанию в Windows с opengl32.dll, Linux с драйверами, не основанными на Mesa, macOS, мобильных платформах и Embedded Linux с EGLFS, но это может измениться. Можно принудительно использовать потоковый рендерер, задав QSG_RENDER_LOOP=threaded в среде.
Непотоковые циклы рендеринга ("basic" и "windows")
Непотоковый цикл рендеринга используется по умолчанию в Windows с ANGLE или нестандартной реализацией opengl32 и Linux с драйверами Mesa. В последнем случае это в основном мера предосторожности, так как не все сочетания драйверов OpenGL и систем окон были протестированы. В то же время такие реализации, как ANGLE или Mesa llvmpipe, вообще не могут нормально работать с потоковым рендерингом, поэтому отсутствие потокового рендеринга для них необходимо.
По умолчанию windows используется для непотокового рендеринга в Windows с ANGLE, в то время как basic используется для всех остальных платформ при необходимости непотокового рендеринга.
Даже при использовании непотокового цикла рендеринга вы должны писать свой код так, как будто вы используете потоковый рендерер, так как несоблюдение этого сделает код непереносимым.
Ниже приведена упрощённая иллюстрация последовательности рендеринга кадров в непотоковом рендерере.
Настройка рендеринга с помощью QQuickRenderControl
При использовании QQuickRenderControl ответственность за управление циклом рендеринга переносится на приложение. В этом случае встроенный цикл рендеринга не используется. Вместо этого приложение должно вызывать шаги обработки, синхронизации и рендеринга в нужное время. Можно реализовать как потоковое, так и непотоковое поведение, аналогичное показанному выше.
Совмещение графа сцены и OpenGL
Граф сцены предоставляет два метода для интеграции контента OpenGL: путём вызова команд OpenGL напрямую и путём создания текстурированного узла в графе сцены.
Подключившись к сигналам QQuickWindow::beforeRendering и QQuickWindow::afterRendering, приложения могут напрямую вызывать OpenGL-вызовы в том же контексте, в котором происходит отрисовка графа сцены. Как указывают названия сигналов, пользователь может отрисовывать контент OpenGL как под Qt Quick-сценой, так и поверх неё. Преимущество интеграции таким образом заключается в том, что для выполнения рендеринга не нужны дополнительные буферы и память. Недостатком является то, что Qt Quick решает, когда вызывать сигналы, и это единственное время, когда приложению OpenGL разрешено рисовать.
Пример графа сцены - OpenGL под QML содержит пример использования этих сигналов.
Другой вариант — создать QQuickFramebufferObject, выполнить отрисовку в него и отобразить его в графе сцены в виде текстуры. Пример Граф сцены — рендеринг FBO показывает, как это можно сделать. Также можно комбинировать несколько контекстов рендеринга и нескольких потоков для создания контента, который будет отображаться в графе сцены. Примеры Граф сцены — рендеринг FBO в потоке демонстрируют, как это можно сделать.
Предупреждение: При смешивании контента OpenGL с рендерингом графа сцены важно, чтобы приложение не оставляло контекст OpenGL в состоянии с привязанными буферами, включёнными атрибутами, специальными значениями в z-буфере или буфере трафарета или аналогичными. Это может привести к непредсказуемому поведению.
Предупреждение: Код отрисовки OpenGL должен быть потокобезопасным, так как рендеринг может происходить вне потока GUI.
Пользовательские элементы с использованием QPainter
Класс QQuickItem предоставляет подкласс QQuickPaintedItem, который позволяет пользователям выполнять рендеринг контента с помощью QPainter.
Предупреждение: Использование QQuickPaintedItem использует косвенную 2D-поверхность для рендеринга контента, либо с помощью программной растризации, либо с помощью буфера кадра OpenGL (FBO), поэтому рендеринг — это операция в два этапа. Сначала растрировать поверхность, затем нарисовать её. Использование API графа сцены напрямую всегда значительно быстрее.
Поддержка ведения журналов
Граф сцены поддерживает ряд категорий ведения журналов. Они могут быть полезны для отслеживания проблем с производительностью и ошибок, а также для помощи участникам Qt.
-
qt.scenegraph.time.texture- записывает время, затраченное на загрузку текстур -
qt.scenegraph.time.compilation- записывает время, затраченное на компиляцию шейдеров -
qt.scenegraph.time.renderer- записывает время, затраченное на различные шаги рендерера -
qt.scenegraph.time.renderloop- записывает время, затраченное на различные шаги цикла рендеринга -
qt.scenegraph.time.glyph- записывает время, затраченное на подготовку глифов полей расстояния -
qt.scenegraph.general- записывает общую информацию о различных частях графа сцены и графической стеке -
qt.scenegraph.renderloop- создаёт подробный журнал различных этапов, участвующих в рендеринге. Этот режим журнала в первую очередь полезен для разработчиков, работающих над Qt.
Бэкенд графа сцены
Помимо публичного API, граф сцены имеет адаптивный слой, который открывает реализацию для аппаратных специфических адаптаций. Это недокументированный, внутренний и закрытый плагин-API, который позволяет группам по адаптации аппаратного обеспечения максимально использовать возможности своего оборудования. Он включает:
- Пользовательские текстуры; конкретно, реализация QQuickWindow::createTextureFromImage и внутреннее представление текстуры, используемое типами Image и BorderImage.
- Пользовательский рендерер; адаптивный слой позволяет плагину решать, как обходить и отрисовывать граф сцены, что позволяет оптимизировать алгоритм рендеринга для конкретного оборудования или использовать расширения, улучшающие производительность.
- Пользовательская реализация графа сцены многих стандартных QML-типов, включая рендеринг текста и шрифтов.
- Пользовательский драйвер анимации; позволяет системе анимации подключаться к вертикальному обновлению низкого уровня отображения для получения плавной отрисовки.
- Пользовательский цикл рендеринга; позволяет лучше контролировать, как QML обрабатывает несколько окон.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/qtquick-visualcanvas-scenegraph.html