Spec-Zone.ru › Qt 6.0

Графическая сцена 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() функцию.

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

Узлы

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

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

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

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

QSGClipNode

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

QSGGeometryNode

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

QSGNode

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

QSGOpacityNode

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

QSGTransformNode

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

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

Предупреждение: Критически важно, чтобы операции с графикой нативных API (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%. Если известно, что система не может обеспечить регулирование частоты кадров на основе вертикальной синхронизации, вместо этого используйте цикл отрисовки 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) или записывается команда отображения, и буферы команд передаются в очередь графики (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 или буфере stencil или аналогичным. Это может привести к непредсказуемому поведению.

Предупреждение: Код пользовательской отрисовки должен быть потокобезопасным в том смысле, что он не должен предполагать выполнения в потоке GUI (главном) приложения.

Пользовательские элементы с использованием QPainter

Класс QQuickItem предоставляет подкласс QQuickPaintedItem, который позволяет пользователям отрисовывать содержимое с помощью QPainter.

Предупреждение: Использование QQuickPaintedItem использует косвенную 2D-поверхность для отрисовки содержимого, используя либо программную растризацию, либо объект OpenGL framebuffer object (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.0/qtquick-visualcanvas-scenegraph.html

Spec-Zone.ru

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