Spec-Zone.ru › Qt 6.1

Граф сцены Qt Quick

Граф сцены в Qt Quick

Qt Quick 2 использует специализированный граф сцены, который затем просматривается и отрисовывается с помощью графического API, такого как OpenGL ES, OpenGL, Vulkan, Metal или Direct 3D. Использование графа сцены для графики вместо традиционных императивных систем рисования (QPainter и подобных) означает, что сцена, подлежащая отрисовке, может сохраняться между кадрами, и весь набор примитивов для отрисовки известен до начала отрисовки. Это открывает ряд возможностей для оптимизации, таких как пакетная отрисовка для минимизации изменений состояния и отбрасывание скрытых примитивов.

Например, предположим, что пользовательский интерфейс содержит список из десяти элементов, где каждый элемент имеет цвет фона, значок и текст. Использование традиционных методов рисования приведет к 30 вызовам отрисовки и аналогичному количеству изменений состояния. Граф сцены, с другой стороны, может переупорядочить примитивы для отрисовки таким образом, что все фоны рисуются одним вызовом, затем все значки, затем весь текст, уменьшая общее количество вызовов отрисовки до всего лишь 3. Такая пакетная обработка и уменьшение изменений состояния могут значительно улучшить производительность на некоторых устройствах.

Граф сцены тесно связан с Qt Quick 2.0 и не может использоваться автономно. Граф сцены управляется и отрисовывается классом QQuickWindow, а пользовательские типы элементов могут добавлять свои графические примитивы в граф сцены с помощью вызова QQuickItem::updatePaintNode().

Граф сцены представляет собой графическое представление сцены элементов, независимой структуры, содержащей достаточную информацию для отрисовки всех элементов. После его настройки им можно управлять и отрисовывать независимо от состояния элементов. На многих платформах граф сцены будет даже отрисовываться в отдельной потоке отрисовки, в то время как поток GUI подготавливает состояние следующего кадра.

Примечание: Большая часть информации на этой странице относится к встроенному, по умолчанию поведению графа сцены Qt Quick. При использовании альтернативной адаптации графа сцены, например, software, не все понятия могут применяться. Более подробную информацию об адаптациях графа сцены см. в разделе Адаптации графа сцены.

Структура графа сцены Qt Quick

Граф сцены состоит из ряда предопределенных типов узлов, каждый из которых выполняет определенную функцию. Хотя мы называем его графом сцены, более точное определение – древовидная структура узлов. Дерево строится из типов QQuickItem в QML-сцене, а затем внутри сцена обрабатывается рендерером, который рисует сцену. Сами узлы не содержат никакого активного кода рисования, ни виртуальной paint() функции.

Несмотря на то, что дерево узлов в основном строится внутренне существующими типами Qt Quick QML, пользователи также могут добавлять полные поддеревья со своим содержимым, включая поддеревья, которые представляют 3D-модели.

Узлы

Самый важный узел для пользователей – QSGGeometryNode. Он используется для определения пользовательской графики путём определения её геометрии и материала. Геометрия определяется с помощью QSGGeometry и описывает форму или сетку графического примитива. Это может быть линия, прямоугольник, многоугольник, множество несвязанных прямоугольников или сложная 3D-сетка. Материал определяет, как заполняются пиксели в этой форме.

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

Примечание: Это ничего не говорит о фактическом порядке отрисовки в рендерере. Гарантируется только визуальный результат.

Доступные узлы:

QSGClipNode

Реализует функцию обрезки в графе сцены

QSGGeometryNode

Используется для всего отрисованного содержимого в графе сцены

QSGNode

Базовый класс для всех узлов в графе сцены

QSGOpacityNode

Используется для изменения непрозрачности узлов

QSGTransformNode

Реализует преобразования в графе сцены

Пользовательские узлы добавляются в граф сцены путём наследования QQuickItem::updatePaintNode() и установкой флага QQuickItem::ItemHasContents.

Предупреждение: Крайне важно, чтобы операции с нативной графикой (OpenGL, Vulkan, Metal и т.д.) и взаимодействие с графом сцены происходили исключительно в потоке отрисовки, в первую очередь во время вызова updatePaintNode(). Правило - использовать только классы с префиксом "QSG" внутри функции QQuickItem::updatePaintNode().

Для получения более подробной информации см. Граф сцены - пользовательская геометрия.

Предварительная обработка

Узлы имеют виртуальную функцию QSGNode::preprocess(), которая вызывается перед отрисовкой графа сцены. Подклассы узлов могут установить флаг QSGNode::UsePreprocess и переопределить функцию QSGNode::preprocess() для выполнения окончательной подготовки своего узла. Например, деление кривой Безье на соответствующий уровень детализации для текущего коэффициента масштаба или обновление части текстуры.

Владение узлами

Владение узлами осуществляется либо явно создателем, либо графом сцены путём установки флага QSGNode::OwnedByParent. Назначение владения графу сцены часто предпочтительнее, поскольку это упрощает очистку, когда граф сцены существует вне потока GUI.

Материалы

Материал описывает, как заполняется внутренняя часть геометрии в QSGGeometryNode. Он инкапсулирует графические шейдеры для вершинных и фрагментных стадий графического конвейера и предоставляет широкую гибкость в достижении целей, хотя большинство самих элементов Qt Quick используют только очень базовые материалы, такие как сплошной цвет и текстурные заполнения.

Пользователи, которые хотят применить пользовательскую затенение к типу QML-элемента, могут сделать это непосредственно в QML, используя тип ShaderEffect.

Ниже приведен полный список классов материалов:

QSGFlatColorMaterial

Удобный способ отрисовки геометрии сплошного цвета в графе сцены

QSGMaterial

Инкапсулирует состояние рендеринга для программы шейдеров

QSGMaterialShader

Представляет собой независимую от графического API программу шейдеров

QSGMaterialType

Используется как уникальный маркер типа в сочетании с QSGMaterial

QSGOpaqueTextureMaterial

Удобный способ отрисовки текстурированной геометрии в графе сцены

QSGTextureMaterial

Удобный способ отрисовки текстурированной геометрии в графе сцены

QSGVertexColorMaterial

Удобный способ отрисовки геометрии с цветом по вершинам в графе сцены

Узлы-утилиты

API графа сцены является низкоуровневым и ориентирован на производительность, а не на удобство использования. Написание пользовательских геометрий и материалов с нуля, даже самых простых, требует немалого кода. По этой причине API включает несколько утилитных классов, чтобы наиболее распространённые пользовательские узлы были легко доступны.

  • QSGSimpleRectNode - подкласс QSGGeometryNode, который определяет прямоугольную геометрию с материалом сплошного цвета.
  • QSGSimpleTextureNode - подкласс QSGGeometryNode, который определяет прямоугольную геометрию с текстурным материалом.

Граф сцены и отрисовка

Отрисовка графа сцены происходит внутри класса QQuickWindow, и нет публичного API для доступа к ней. Однако есть несколько мест в конвейере отрисовки, где пользователь может подключить код приложения. Это можно использовать для добавления пользовательского содержимого графа сцены или вставки произвольных команд отрисовки, вызывая непосредственно графический API (OpenGL, Vulkan, Metal и т.д.), используемый графом сцены. Точки интеграции определяются циклом отрисовки.

Подробное описание работы рендерера графа сцены см. в разделе По умолчанию рендерер графа сцены Qt Quick.

Доступны два варианта цикла отрисовки: basic, и threaded. basic является однопоточным, в то время как threaded выполняет отрисовку графа сцены в отдельном потоке. Qt пытается выбрать подходящий цикл в зависимости от платформы и, возможно, графических драйверов. Если это не устраивает или для целей тестирования, можно использовать переменную среды QSG_RENDER_LOOP для принудительного использования заданного цикла. Для проверки используемого цикла отрисовки включите qt.scenegraph.general категорию регистрации.

Примечание: Цикл отрисовки threaded полагается на реализацию графического API для регулирования, например, запрашивая интервал переключения в 1 в случае OpenGL. Некоторые графические драйверы позволяют пользователям переопределять это значение и отключать его, игнорируя запрос Qt. Без блокировки в операции обмена буферами (или в других местах), цикл отрисовки будет запускать анимации слишком быстро и загружать ЦП на 100%. Если известно, что система не может обеспечить регулирование на основе vsync, используйте цикл отрисовки basic вместо этого, установив QSG_RENDER_LOOP=basic в среде.

Многопоточный цикл отрисовки ('threaded')

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

Ниже приведён простой обзор того, как кадр отрисовывается с помощью потоковой очереди отрисовки и OpenGL. Эти шаги одинаковы и для других графических API, за исключением особенностей контекста OpenGL.

  1. В сцене QML происходит изменение, вызывая вызов QQuickItem::update(). Это может быть результатом, например, анимации или пользовательского ввода. В поток отрисовки публикуется событие для запуска нового кадра.
  2. Поток отрисовки готовится к отрисовке нового кадра и блокирует поток GUI.
  3. Пока поток отрисовки подготавливает новый кадр, поток GUI вызывает QQuickItem::updatePolish() для окончательной доработки элементов перед их отрисовкой.
  4. Поток GUI заблокирован.
  5. Издаётся сигнал QQuickWindow::beforeSynchronizing(). Приложения могут устанавливать прямые соединения (используя Qt::DirectConnection) с этим сигналом, чтобы выполнить все необходимые подготовительные действия перед вызовами QQuickItem::updatePaintNode().
  6. Синхронизация состояния QML в графическом дереве. Это делается путём вызова функции QQuickItem::updatePaintNode() для всех элементов, которые изменились с момента предыдущего кадра. Это единственный момент, когда элементы QML и узлы графического дерева взаимодействуют.
  7. Блокировка потока GUI освобождена.
  8. Отрисовывается графическое дерево:
    1. Издаётся сигнал QQuickWindow::beforeRendering(). Приложения могут устанавливать прямые соединения (используя Qt::DirectConnection) с этим сигналом, чтобы использовать пользовательские вызовы графического API, которые будут отображаться визуально под сценой QML.
    2. Элементы, которые указали флаг QSGNode::UsePreprocess, получат вызов функции QSGNode::preprocess().
    3. Рендерер обрабатывает узлы.
    4. Рендерер генерирует состояния и записывает вызовы отрисовки для используемого графического API.
    5. Издаётся сигнал QQuickWindow::afterRendering(). Приложения могут устанавливать прямые соединения (используя Qt::DirectConnection) с этим сигналом, чтобы выполнить пользовательские вызовы графического API, которые будут отображаться визуально поверх сцены QML.
    6. Кадр готов. Буферы меняются (OpenGL), или команда present записывается, и буферы команд отправляются в очередь графического процессора (Vulkan, Metal). Издаётся QQuickWindow::frameSwapped().
  9. Пока поток отрисовки выполняет отрисовку, поток GUI свободен для продвижения анимаций, обработки событий и т.д.

Потоковый рендерер используется по умолчанию в Windows с Direct3D 11 и OpenGL при использовании opengl32.dll, Linux (исключая Mesa llvmpipe), macOS с Metal, мобильных платформах и Embedded Linux с EGLFS, а также с Vulkan независимо от платформы. Всё это может измениться в будущих выпусках. Всегда можно принудительно использовать потоковый рендерер, установив QSG_RENDER_LOOP=threaded в среде.

Непотоковая очередь отрисовки ('basic')

Непотоковая очередь отрисовки используется по умолчанию в Windows с OpenGL, когда не используется стандартная библиотека opengl32.dll, macOS с OpenGL и Linux с некоторыми драйверами. В последнем случае это в основном мера предосторожности, так как не все сочетания драйверов OpenGL и систем окон были протестированы.

На macOS и OpenGL, потоковая очередь отрисовки не поддерживается при компиляции с XCode 10 (10.14 SDK) или более поздними версиями, так как это активирует поддержку прослойки-бэкенда на macOS 10.14. Вы можете скомпилировать с Xcode 9 (10.13 SDK), чтобы отключить поддержку прослойки-бэкенда, в этом случае потоковая очередь отрисовки доступна и используется по умолчанию. Таких ограничений нет для Metal.

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

Ниже приведена упрощённая иллюстрация последовательности отрисовки кадра в непотоковом рендерере.

Настройка управления отрисовкой с помощью QQuickRenderControl

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

Смешивание графического дерева и родного графического API

Графическое дерево предлагает два метода интеграции команд отрисовки, предоставляемых приложением: вызов команд OpenGL, Vulkan, Metal и т.д. напрямую и создание текстурного узла в графическом дереве.

Подключив к сигналам QQuickWindow::beforeRendering() и QQuickWindow::afterRendering(), приложения могут непосредственно вызывать OpenGL-вызовы в том же контексте, что и графическое дерево. С такими API, как Vulkan или Metal, приложения могут получать доступ к родным объектам, таким как буфер команд графического дерева, через QSGRendererInterface и записывать в него команды по мере необходимости. Как указывают имена сигналов, пользователь может отрисовывать содержимое как под, так и над сценой Qt Quick. Преимущество такой интеграции заключается в том, что для отрисовки не требуется дополнительный буфер или память, и, возможно, дорогостоящий шаг текстурирования исключается. Недостаток заключается в том, что Qt Quick решает, когда вызывать сигналы, и это единственное время, когда приложение OpenGL разрешено рисовать.

Пример графического дерева — OpenGL под QML демонстрирует использование этих сигналов с OpenGL.

Пример графического дерева — Direct3D 11 под QML демонстрирует использование этих сигналов с Direct3D.

Пример графического дерева — Metal под QML демонстрирует использование этих сигналов с Metal.

Пример графического дерева — Vulkan под QML демонстрирует использование этих сигналов с Vulkan.

Другой вариант, доступный только для OpenGL, заключается в создании QQuickFramebufferObject, отрисовке в него и отображении его в графическом дереве как текстуры. Пример графического дерева — Отрисовка FBO показывает, как это можно сделать.

Графические API, отличные от OpenGL, также могут следовать этому подходу, хотя QQuickFramebufferObject в настоящее время их не поддерживает. Создание и отрисовка текстуры напрямую с помощью базового API, за которым следует обертывание и использование этого ресурса в сцене Qt Quick в пользовательском элементе QQuickItem, показано в Примере графического дерева — Импорт текстур Metal. В этом примере используется Metal, но концепции применимы ко всем остальным графическим API.

Предупреждение: Начиная с Qt 6.0, прямое использование базового графического API должно быть заключено в вызов QQuickWindow::beginExternalCommands() и QQuickWindow::endExternalCommands(). Эта концепция может быть знакома по QPainter::beginNativePainting(), и она выполняет аналогичную функцию: она позволяет графическому дереву Qt Quick распознать, что любые кэшированные состояния и предположения о состоянии в текущем записываемом проходе отрисовки, если он есть, теперь недействительны, потому что код приложения мог их изменить, работая напрямую с базовым графическим API.

Предупреждение: При смешивании контента OpenGL с отрисовкой графического дерева важно, чтобы приложение не оставляло контекст OpenGL в состоянии, когда связаны буферы, включены атрибуты, установлены специальные значения в z-буфере или буфере обрезки, или аналогичные. Это может привести к непредсказуемому поведению.

Предупреждение: Код пользовательской отрисовки должен учитывать потоки в том смысле, что он не должен предполагать, что он выполняется в потоке 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.

Доступна и устаревшая переменная среды QSG_INFO. Установка её в ненулевое значение включает категорию qt.scenegraph.general.

Примечание: При возникновении проблем с графикой или при сомнениях в том, какая очередь отрисовки или графический API используется, всегда запускайте приложение, по крайней мере, с включёнными qt.scenegraph.general и qt.rhi.*, или с установленным значением QSG_INFO=1. Это позволит вывести некоторую важную информацию на вывод отладки во время инициализации.

Бэкенд графического дерева

Помимо публичного 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-6.1/qtquick-visualcanvas-scenegraph.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API