Граф сцен 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 готовит состояние следующего кадра.
Примечание: Большая часть информации на этой странице относится к адаптации графа сцен Qt Quick, использующей OpenGL по умолчанию. Дополнительную информацию о различных адаптациях графа сцен см. в Адаптации графа сцен.
Структура графа сцен 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, это можно сделать непосредственно в 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 в среде.
Потоковый цикл рендеринга ("threaded")
На многих конфигурациях отрисовка графического дерева сцены происходит в отдельной потоковой нити отрисовки. Это делается для повышения параллельности многоядерных процессоров и более эффективного использования временных задержек, таких как ожидание вызова буфера обмена блокировки. Это обеспечивает значительное повышение производительности, но накладывает определённые ограничения на то, где и когда можно взаимодействовать с графическим деревом сцены.
Ниже приведён простой обзор того, как кадр формируется с помощью потоковой нити отрисовки.
- Происходит изменение в QML-сцене, вызывая
QQuickItem::update(). Это может быть результатом, например, анимации или пользовательского ввода. В поток отрисовки отправляется событие для инициации нового кадра. - Поток отрисовки готовится к отрисовке нового кадра, делает OpenGL-контекст текущим и инициирует блокировку в потоке пользовательского интерфейса.
- Пока поток отрисовки готовит новый кадр, поток пользовательского интерфейса вызывает QQuickItem::updatePolish(), чтобы выполнить окончательную доработку элементов перед их отрисовкой.
- Поток пользовательского интерфейса заблокирован.
- Сигнал QQuickWindow::beforeSynchronizing() отправляется. Приложения могут установить прямые подключения (используя Qt::DirectConnection) к этому сигналу для выполнения необходимой подготовки перед вызовами QQuickItem::updatePaintNode().
- Синхронизация состояния QML в графическое дерево сцены. Это выполняется путём вызова функции QQuickItem::updatePaintNode() для всех элементов, которые изменились с момента предыдущего кадра. Это единственный момент взаимодействия QML-элементов и узлов в графическом дереве сцены.
- Блокировка потока пользовательского интерфейса снимается.
- Отрисовывается графическое дерево сцены:
- Отправляется сигнал QQuickWindow::beforeRendering(). Приложения могут установить прямые подключения (используя Qt::DirectConnection) к этому сигналу для использования пользовательских OpenGL-вызовов, которые затем будут накладываться визуально под QML-сценой.
- Элементы, для которых задан QSGNode::UsePreprocess, получат вызов функции QSGNode::preprocess().
- Отрисовщик обрабатывает узлы и вызывает OpenGL-функции.
- Отправляется сигнал QQuickWindow::afterRendering(). Приложения могут установить прямые подключения (используя Qt::DirectConnection) к этому сигналу для использования пользовательских OpenGL-вызовов, которые затем будут накладываться визуально поверх QML-сцены.
- Отображаемый кадр меняется, и отправляется QQuickWindow::frameSwapped().
- Пока поток отрисовки выполняет отрисовку, поток пользовательского интерфейса свободен для продвижения анимации, обработки событий и т.д.
Потоковая нить отрисовки используется по умолчанию на 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-код отрисовки должен учитывать работу с потоками, поскольку отрисовка может происходить вне потока пользовательского интерфейса.
Пользовательские элементы с использованием 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/qt-5.9/qtquick-visualcanvas-scenegraph.html