Qt Quick Scene Graph По умолчанию рендерер сцены
Этот документ объясняет, как работает рендерер сцены по умолчанию, чтобы можно было писать код, который использует его оптимальным образом, как по производительности, так и по функциональности.
Чтобы получить хорошую производительность, не нужно понимать внутреннее устройство рендерера. Однако это может помочь при интеграции с графическим деревом или при выяснении, почему невозможно извлечь максимальную эффективность из графического чипа.
Примечание: Даже в случае, когда кадр уникален в каждом случае и все загружается с нуля, рендерер по умолчанию будет работать хорошо.
Элементы Qt Quick в сцене QML заполняют дерево экземпляров QSGNode. После создания это дерево — полное описание того, как должен быть отрисован определённый кадр. Оно вообще не содержит ссылок на элементы Qt Quick и на большинстве платформ будет обрабатываться и отрисовываться в отдельном потоке. Рендерер — это автономная часть графического дерева, которая обходит дерево QSGNode и использует геометрию, определённую в QSGGeometryNode, и состояние шейдера, определённое в QSGMaterial, для обновления состояния графики и генерации вызовов отрисовки.
При необходимости рендерер можно полностью заменить с помощью API внутреннего бэкэнда графического дерева. Это в основном интересно для поставщиков платформ, которые хотят использовать нестандартные функции аппаратного обеспечения. Для большинства случаев использования рендерер по умолчанию будет достаточным.
Рендерер по умолчанию фокусируется на двух основных стратегиях оптимизации отрисовки: объединение вызовов отрисовки и сохранение геометрии на видеокарте.
Объединение вызовов
В то время как традиционный 2D API, такой как QPainter, Cairo или Context2D, написан для обработки тысяч отдельных вызовов отрисовки в кадр, OpenGL и другие ускоренные API аппаратного обеспечения работают лучше всего, когда количество вызовов отрисовки очень низкое, а изменения состояния минимальны.
Примечание: Хотя OpenGL используется в качестве примера в следующих разделах, те же концепции относятся и к другим API графики.
Рассмотрим следующий случай использования:
Самый простой способ отображения этого списка — на основе ячеек. Сначала рисуется фон. Это прямоугольник определенного цвета. В терминах OpenGL это означает выбор программы шейдера для заливки сплошным цветом, настройку цвета заливки, настройку матрицы преобразования, содержащей смещения по x и y, а затем, например, glDrawArrays, чтобы нарисовать два треугольника, образующие прямоугольник. Затем рисуется значок. В терминах OpenGL это означает выбор программы шейдера для отрисовки текстур, выбор активной текстуры для использования, настройку матрицы преобразования, включение альфа-смешивания и затем, например, glDrawArrays, чтобы нарисовать два треугольника, образующих границу прямоугольника значка. Текст и разделительная линия между ячейками следуют аналогичному шаблону. И этот процесс повторяется для каждой ячейки в списке, поэтому для более длинного списка накладные расходы, накладываемые на изменения состояния и вызовы отрисовки OpenGL, полностью перевешивают преимущества использования ускоренного API аппаратного обеспечения.
Когда каждый примитив большой, эти накладные расходы незначительны, но в случае типичного пользовательского интерфейса есть много небольших элементов, которые суммируются в значительные накладные расходы.
Рендерер сцены по умолчанию работает в рамках этих ограничений и будет пытаться объединить отдельные примитивы в пакеты, сохраняя при этом точный визуальный результат. В результате получается меньше изменений состояния OpenGL и минимальное количество вызовов отрисовки, что обеспечивает оптимальную производительность.
Непрозрачные примитивы
Рендерер различает непрозрачные примитивы и примитивы, требующие альфа-смешивания. Используя буфер глубины OpenGL и присваивая каждому примитиву уникальную позицию z, рендерер может свободно менять порядок непрозрачных примитивов, не обращая внимания на их расположение на экране и то, какие другие элементы они перекрывают. На основе состояния материала каждого примитива рендерер создаст пакеты непрозрачных элементов. Из набора элементов ядра Qt Quick это включает элементы Rectangle с непрозрачными цветами и полностью непрозрачными изображениями, такими как JPEG или BMP.
Другое преимущество использования непрозрачных примитивов заключается в том, что для непрозрачных примитивов не нужно включать GL_BLEND, что может быть довольно дорогостоящим, особенно на мобильных и встроенных видеокартах.
Непрозрачные примитивы отрисовываются спереди назад с включёнными glDepthMask и GL_DEPTH_TEST. На видеокартах, которые внутренне выполняют ранние проверки глубины, это означает, что фрагментный шейдер не должен выполняться для пикселей или блоков пикселей, которые скрыты. Имейте в виду, что рендерер всё равно должен учитывать эти узлы, и вершинный шейдер всё равно выполняется для каждой вершины в этих примитивах, поэтому, если приложение знает, что что-то полностью скрыто, лучше всего явно скрыть его с помощью Item::visible или Item::opacity.
Примечание: Item::z используется для управления порядком элементов относительно их соседей. Он не имеет непосредственной связи с рендерером и буфером глубины OpenGL.
Примитивы с альфа-смешиванием
После отрисовки непрозрачных примитивов рендерер отключит glDepthMask, включит GL_BLEND и отрисует все примитивы с альфа-смешиванием сзади вперёд.
Объединение примитивов с альфа-смешиванием требует от рендерера немного больше усилий, так как элементы, которые перекрываются, должны быть отрисованы в правильном порядке, чтобы альфа-смешивание выглядело правильно. Только на буфере глубины полагаться недостаточно. Рендерер выполняет проход по всем примитивам с альфа-смешиванием и рассматривает их прямоугольники ограничительной рамки помимо их состояния материала, чтобы определить, какие элементы можно объединить, а какие нет.
В случае слева синие фоны можно нарисовать одним вызовом, а два текстовых элемента — другим, так как тексты перекрывают только фон, перед которым они расположены. В правом случае фон «Item 4» перекрывает текст «Item 3», поэтому в этом случае каждый из фонов и текстов необходимо отрисовывать с помощью отдельных вызовов.
С точки зрения z, альфа-примитивы чередуются с непрозрачными узлами и могут инициировать ранние проверки глубины, если доступны, но снова, установка Item::visible в false всегда быстрее.
Смешение с 3D примитивами
Графическое дерево может поддерживать псевдо-3D и собственные 3D-примитивы. Например, можно реализовать эффект «перегиба страницы» с помощью ShaderEffect или реализовать тор с нанесённой нормалью с помощью QSGGeometry и настраиваемого материала. При этом нужно учитывать, что рендерер по умолчанию уже использует буфер глубины.
Рендерер изменяет вершинный шейдер, возвращаемый из QSGMaterialShader::vertexShader(), и сжимает значения z вершин после применения матриц модели-представления и проекции, а затем добавляет небольшое смещение по z, чтобы установить его в правильную позицию z.
Сжатие предполагает, что значения z находятся в диапазоне от 0 до 1.
Атлас текстур
Активная текстура — уникальное состояние OpenGL, поэтому несколько примитивов, использующих разные текстуры OpenGL, нельзя объединить. Поэтому графическое дерево Qt Quick позволяет создавать несколько экземпляров QSGTexture как меньших подрегионов более крупной текстуры; атласа текстур.
Главное преимущество атласов текстур заключается в том, что несколько экземпляров QSGTexture теперь ссылаются на один и тот же экземпляр текстуры OpenGL. Это позволяет объединять также вызовы отрисовки с текстурами, такие как элементы Image, элементы BorderImage, элементы ShaderEffect, а также типы C++, такие как QSGSimpleTextureNode, и пользовательские QSGGeometryNodes, использующие текстуры.
Примечание: Крупные текстуры не попадают в атлас текстур.
Текстуры на основе атласа создаются путём передачи QQuickWindow::TextureCanUseAtlas в QQuickWindow::createTextureFromImage().
Примечание: Текстуры на основе атласа не имеют текстурных координат в диапазоне от 0 до 1. Используйте QSGTexture::normalizedTextureSubRect(), чтобы получить текстурные координаты атласа.
Графическое дерево использует эвристические методы, чтобы определить, каким должен быть размер атласа и какой порог размера для включения в атлас. Если необходимы другие значения, можно переопределить их с помощью переменных среды QSG_ATLAS_WIDTH=[width], QSG_ATLAS_HEIGHT=[height] и QSG_ATLAS_SIZE_LIMIT=[size]. Изменение этих значений в основном будет интересно для поставщиков платформ.
Корни пакетов
Помимо объединения совместимых примитивов в пакеты, рендерер по умолчанию также пытается минимизировать объём данных, которые необходимо отправлять на видеокарту для каждого кадра. Рендерер по умолчанию определяет поддеревья, которые принадлежат вместе, и пытается поместить их в отдельные пакеты. После определения пакетов они объединяются, загружаются и хранятся в памяти видеокарты с использованием объектов буфера вершин.
Узлы преобразования
Каждый элемент Qt Quick вставляет QSGTransformNode в дерево сцены для управления его x, y, масштабированием или поворотом. Элементы дочерних элементов будут размещены под этим узлом преобразования. Рендерер по умолчанию отслеживает состояние узлов преобразования между кадрами и будет рассматривать поддеревья, чтобы определить, является ли узел преобразования хорошим кандидатом на то, чтобы стать корнем набора пакетов. Узел преобразования, который изменяется между кадрами и у которого есть довольно сложное поддерево, может стать корнем пакета.
QSGGeometryNodes в поддереве корня пакета предварительно преобразуются относительно корня на процессоре. Затем они загружаются и сохраняются на видеокарте. При изменении преобразования рендереру нужно только обновить матрицу корня, а не каждый отдельный элемент, что делает прокрутку списков и таблиц очень быстрой. В последующих кадрах, если узлы не добавляются и не удаляются, отрисовка списка фактически бесплатна. При вводе нового содержимого в поддерево пакет, который его получает, перестраивается, но это всё равно относительно быстро. Обычно несколько неизменяемых кадров приходится на каждый кадр с добавленными или удалёнными узлами при прокрутке сетки или списка.
Другое преимущество определения узлов преобразования в качестве корней пакетов заключается в том, что это позволяет рендереру сохранять части дерева, которые не изменились. Например, предположим, что пользовательский интерфейс состоит из списка и строки кнопок. Когда список прокручивается и делегаты добавляются и удаляются, остальная часть пользовательского интерфейса, строка кнопок, неизменна и может быть отрисована с использованием геометрии, уже сохранённой на видеокарте.
Порог узла и вершины для того, чтобы узел преобразования стал корнем пакета, может быть переопределён с помощью переменных среды QSG_RENDERER_BATCH_NODE_THRESHOLD=[count] и QSG_RENDERER_BATCH_VERTEX_THRESHOLD=[count]. Переопределение этих флагов будет в основном полезно для поставщиков платформ.
Примечание: Под корнем пакета создаётся один пакет для каждого уникального набора состояния материала и типа геометрии.
Обрезка
При установке Item::clip в значение true, будет создан QSGClipNode с прямоугольником в его геометрии. По умолчанию рендерер будет применять этот обрезку, используя цизирование в OpenGL. Если элемент повернут на угол, не равный 90 градусам, используется буфер трафарета OpenGL. Элемент Qt Quick Item поддерживает установку только прямоугольника в качестве обрезки через QML, но API графа сцены и рендерер по умолчанию могут использовать любую форму для обрезки.
При применении обрезки к поддереву, это поддерево должно быть отрисовано с уникальным состоянием OpenGL. Это означает, что при Item::clip = true, кэширование этого элемента ограничено его дочерними элементами. При большом количестве дочерних элементов, таких как ListView или GridView, или сложных дочерних элементов, таких как TextArea, это нормально. Однако следует осторожно использовать обрезку для небольших элементов, так как это препятствует кэшированию. Это включает метки кнопок, текстовые поля или делегаты списков и ячейки таблицы.
Буферы вершин
Каждый пакет использует объект буфера вершин (VBO) для хранения данных на графическом процессоре. Этот буфер вершин сохраняется между кадрами и обновляется, когда изменяется часть графа сцены, которую он представляет.
По умолчанию рендерер загрузит данные в VBO, используя GL_STATIC_DRAW. Можно выбрать другую стратегию загрузки, установив переменную среды QSG_RENDERER_BUFFER_STRATEGY=[strategy]. Допустимые значения: stream и dynamic. Изменение этого значения в основном полезно для поставщиков платформ.
Антиалиасинг
Граф сцены поддерживает два типа антиалиасинга. По умолчанию примитивы, такие как прямоугольники и изображения, будут антиалиасированы путем добавления дополнительных вершин по краю примитивов, так что края плавно переходят в прозрачность. Мы называем этот метод антиалиасингом вершин. Если пользователь запросит контекст OpenGL с многократной выборкой, установив QSurfaceFormat с количеством выборок, большим чем 0 с помощью QQuickWindow::setFormat(), граф сцены отдаст предпочтение антиалиасингу на основе многократной выборки (MSAA). Эти два метода повлияют на то, как происходит отрисовка внутри, и имеют разные ограничения.
Также можно переопределить метод антиалиасинга, установив переменную среды QSG_ANTIALIASING_METHOD в значение vertex или msaa.
Антиалиасинг вершин может создавать швы между краями смежных примитивов, даже когда два края математически одинаковы. Антиалиасинг с многократной выборкой этого не делает.
Антиалиасинг вершин
Антиалиасинг вершин можно включить и выключить для каждого элемента, используя свойство Item::antialiasing. Он будет работать независимо от того, поддерживает ли это подлежащее оборудование, и обеспечивает более качественный антиалиасинг, как для обычных отрисовываемых примитивов, так и для примитивов, захваченных в буферы кадров, например, с помощью типа ShaderEffectSource.
Недостатком использования антиалиасинга вершин является то, что каждый примитив с включенным антиалиасингом должен быть смешан. С точки зрения кэширования это означает, что рендереру нужно больше работы, чтобы выяснить, можно ли кэшировать примитив или нет, и из-за перекрытий с другими элементами сцены это также может привести к меньшему кэшированию, что может повлиять на производительность.
На устройствах с низкой производительностью смешивание также может быть достаточно дорогим, поэтому для изображения или скругленного прямоугольника, покрывающих большую часть экрана, количество смешивания, необходимое для внутренней части этих примитивов, может привести к значительной потере производительности, так как весь примитив должен быть смешан.
Антиалиасинг с многократной выборкой
Антиалиасинг с многократной выборкой — это функция оборудования, в которой оборудование вычисляет значение покрытия на каждый пиксель в примитиве. Некоторые устройства могут выполнять многократную выборку с очень низкой стоимостью, в то время как другим устройствам может потребоваться больше памяти и больше циклов графического процессора для отрисовки кадра.
Используя антиалиасинг с многократной выборкой, многие примитивы, такие как скругленные прямоугольники и элементы изображений, могут быть антиалиасированы и оставаться *непрозрачными* в графе сцены. Это означает, что рендерер легче работает при создании пакетов и может полагаться на раннюю проверку Z, чтобы избежать избыточного отображения.
При использовании антиалиасинга с многократной выборкой для содержимого, отрисовываемого в буферы кадров, требуются дополнительные расширения для поддержки многократной выборки буферов кадров. Обычно GL_EXT_framebuffer_multisample и GL_EXT_framebuffer_blit. Большинство настольных процессоров имеют эти расширения, но они менее распространены в встраиваемых процессорах. Если многократная выборка буфера кадров недоступна в оборудовании, содержимое, отрисованное в буферы кадров, не будет антиалиасировано, включая содержимое ShaderEffectSource.
Производительность
Как было сказано в начале, для достижения хорошей производительности не нужно понимать тонкие детали рендерера. Он разработан для оптимизации типовых случаев использования и будет работать довольно хорошо практически во всех ситуациях.
- Хорошая производительность достигается за счет эффективного кэширования с минимальным количеством геометрии, загружаемой снова и снова. Установив переменную среды
QSG_RENDERER_DEBUG=render, рендерер выведет статистику о том, насколько хорошо происходит кэширование, сколько используется пакетов, какие пакеты сохраняются, а какие непрозрачны и нет. При стремлении к оптимальной производительности загрузки должны выполняться только тогда, когда это действительно необходимо, пакетов должно быть меньше 10, и по крайней мере 3-4 из них должны быть непрозрачными. - По умолчанию рендерер не выполняет обрезку области просмотра и обнаружение непрозрачности на стороне процессора. Если что-то не должно быть видно, оно не должно отображаться. Используйте
Item::visible: falseдля элементов, которые не должны быть нарисованы. Основная причина, по которой такая логика не добавляется, заключается в том, что это добавляет дополнительные затраты, которые также повредят приложениям, позаботившимся о хорошем поведении. - Убедитесь, что используется атлас текстур. Элементы Image и BorderImage будут использовать его, если изображение не слишком большое. Для текстур, созданных в C++, передайте QQuickWindow::TextureCanUseAtlas при вызове QQuickWindow::createTexture(). Установив переменную среды
QSG_ATLAS_OVERLAY, все текстуры атласа будут раскрашены, чтобы их было легко идентифицировать в приложении. - В тех случаях, где это возможно, используйте непрозрачные примитивы. Непрозрачные примитивы быстрее обрабатываются рендерером и быстрее рисуются на графическом процессоре. Например, файлы PNG часто имеют альфа-канал, даже если каждый пиксель полностью непрозрачен. Файлы JPG всегда непрозрачны. При предоставлении изображений в QQuickImageProvider или создании изображений с помощью QQuickWindow::createTextureFromImage(), сделайте формат изображения QImage::Format_RGB32, если это возможно.
- Следите за тем, что перекрывающиеся составные элементы, как на иллюстрации выше, не могут быть кэшированы.
- Обрезка нарушает кэширование. Никогда не используйте её на уровне элемента, внутри ячеек таблицы, делегатов элементов или аналогичных. Вместо обрезки текста используйте обрезание. Вместо обрезки изображения создайте QQuickImageProvider, возвращающий обрезанное изображение.
- Кэширование работает только для 16-битных индексов. Все встроенные элементы используют 16-битные индексы, но пользовательская геометрия также может использовать 32-битные индексы.
- Некоторые флаги материалов препятствуют кэшированию, наиболее ограничивающим из которых является QSGMaterial::RequiresFullMatrix, который предотвращает все кэширование.
- Приложения с однотонным фоном должны устанавливать его с помощью QQuickWindow::setColor(), а не используя верхний прямоугольник. QQuickWindow::setColor() будет использоваться в вызове
glClear(), что потенциально быстрее. - Элементы изображения с mipmapping не размещаются в глобальном атласе и не будут кэшироваться.
- Ошибка в драйвере OpenGL, связанная с чтением из буфера кадров (FBO), может привести к повреждению отрисованных глифов. Если вы установите переменную среды
QML_USE_GLYPHCACHE_WORKAROUND, Qt сохранит дополнительную копию глифа в оперативной памяти. Это означает, что производительность немного ниже при отрисовке глифов, которые не были нарисованы ранее, поскольку Qt обращается к дополнительной копии через процессор. Это также означает, что кэш глифов будет использовать в два раза больше памяти. Качество при этом не страдает.
Если приложение работает плохо, убедитесь, что рендеринг является узким местом. Используйте профайлер! Переменная среды QSG_RENDER_TIMING=1 выведет ряд полезных временных параметров, которые могут быть полезны для определения места проблемы.
Визуализация
Для визуализации различных аспектов рендерера графа сцены по умолчанию можно установить переменную среды QSG_VISUALIZE в одно из значений, описанных в каждом разделе ниже. Мы предоставляем примеры вывода некоторых переменных, используя следующий код QML:
import QtQuick 2.2
Rectangle {
width: 200
height: 140
ListView {
id: clippedList
x: 20
y: 20
width: 70
height: 100
clip: true
model: ["Item A", "Item B", "Item C", "Item D"]
delegate: Rectangle {
color: "lightblue"
width: parent.width
height: 25
Text {
text: modelData
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
ListView {
id: clippedDelegateList
x: clippedList.x + clippedList.width + 20
y: 20
width: 70
height: 100
clip: true
model: ["Item A", "Item B", "Item C", "Item D"]
delegate: Rectangle {
color: "lightblue"
width: parent.width
height: 25
clip: true
Text {
text: modelData
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
} Для ListView слева мы устанавливаем свойство clip в true. Для ListView справа мы также устанавливаем свойство clip каждого делегата в true для иллюстрации эффектов обрезки на кэширование.
Оригинал
Примечание: Визуализируемые элементы не учитывают обрезку, и порядок отрисовки произвольный.
Визуализация пакетов
Установка QSG_VISUALIZE в значение batches визуализирует пакеты в рендерере. Объединенные пакеты отрисовываются сплошным цветом, а не объединенные пакеты — диагональным узором. Малое количество уникальных цветов означает хорошее кэширование. Необъединенные пакеты плохие, если они содержат много отдельных узлов.
QSG_VISUALIZE=batches
Визуализация обрезки
Установка QSG_VISUALIZE в значение clip отрисовывает красные области поверх сцены, чтобы указать обрезку. Поскольку элементы Qt Quick по умолчанию не обрезаются, обычно обрезка не визуализируется.
QSG_VISUALIZE=clip
Визуализация изменений
Установка QSG_VISUALIZE в значение changes визуализирует изменения в рендерере. Изменения в графе сцены визуализируются вспыхивающим наложением случайного цвета. Изменения в примитиве визуализируются сплошным цветом, а изменения в предке, такие как изменения матрицы или непрозрачности, визуализируются узором.
Визуализация избыточного отображения
Установка QSG_VISUALIZE в значение overdraw визуализирует избыточное отображение в рендерере. Визуализируйте все элементы в 3D, чтобы выделить избыточное отображение. Этот режим также может быть использован для обнаружения геометрии за пределами области просмотра до некоторой степени. Непрозрачные элементы отображаются с зелёным оттенком, а полупрозрачные — с красным. Границы области просмотра отрисовываются синим цветом. Непрозрачное содержимое легче обрабатывается графом сцены и обычно быстрее отрисовывается.
Обратите внимание, что корневой прямоугольник в коде выше излишен, так как окно также белое, поэтому отрисовка прямоугольника — пустая трата ресурсов в данном случае. Изменение его на элемент может немного улучшить производительность.
QSG_VISUALIZE=overdraw
Отображение через интерфейс аппаратного ускорения рендеринга Qt
Начиная с Qt 6.0, адаптация по умолчанию всегда выполняет рендеринг через слой абстракции графики, интерфейс аппаратного ускорения рендеринга Qt (RHI), предоставляемый модулем QtGui. Это означает, что, в отличие от Qt 5, сценарий не выполняет непосредственные вызовы OpenGL. Вместо этого он записывает команды ресурсов и отрисовки, используя API RHI, которые затем переводят поток команд в вызовы OpenGL, Vulkan, Metal или Direct3D. Обработка шейдеров также унифицирована, поскольку код шейдеров пишется один раз, компилируется в SPIR-V, а затем преобразуется в язык, соответствующий различным графическим API.
Для управления поведением можно использовать следующие переменные среды:
| Переменная среды | Возможные значения | Описание |
|---|---|---|
QSG_RHI_BACKEND |
vulkan, metal, opengl, d3d11
|
Запрашивает конкретный бэкэнд RHI. По умолчанию целевой графический API выбирается на основе платформы, если это не переопределено этой переменной или эквивалентными API C++. В настоящее время по умолчанию используется Direct3D 11 для Windows, Metal для macOS и OpenGL в других случаях. |
QSG_INFO |
1 |
Как и при пути рендеринга на основе OpenGL, установка этого параметра позволяет выводить системную информацию при инициализации сцены Qt Quick. Это может быть очень полезно для устранения неполадок. |
QSG_RHI_DEBUG_LAYER |
1 |
Применимо (Vulkan, Direct3D), включает слои отладки и/или валидации реализации графического API, если они доступны. |
QSG_RHI_PREFER_SOFTWARE_RENDERER |
1 |
Запрашивает выбор адаптера или физического устройства, использующего растрирование на основе программного обеспечения. Применимо только в том случае, если базовым API поддерживается перечисление адаптеров (например, Direct3D или Vulkan), и в противном случае игнорируется. |
Приложения, желающие всегда работать с одним заданным графическим API, могут запросить это через C++ также. Например, следующий вызов, сделанный на ранней стадии функции main() до создания любого объекта QQuickWindow, принудительно использует Vulkan (и в противном случае завершится ошибкой):
QQuickWindow::setGraphicsApi(QSGRendererInterface::Vulkan);
См. QSGRendererInterface::GraphicsApi. Значения перечисления OpenGL, Vulkan, Metal, Direct3D11 эквивалентны по результату выполнению с QSG_RHI_BACKEND, установленным на соответствующий строковый ключ.
Все бэкэнды QRhi будут выбирать системный адаптер GPU или физическое устройство по умолчанию, если это не переопределено переменной QSG_RHI_PREFER_SOFTWARE_RENDERER или переменной, специфичной для бэкэнда, например, QT_D3D_ADAPTER_INDEX или QT_VK_PHYSICAL_DEVICE_INDEX. В настоящее время дальнейшая настройка адаптеров не предоставляется.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.1/qtquick-visualcanvas-scenegraph-renderer.html