Запуск Qt Quick на Vulkan, Metal и Direct3D – Часть 3

Windows, Qt, OpenGL, Vulkan, macOS, Quick, Graphics, Embedded, Desktop

В третьей части серии статей о графике Qt рассмотрим, как обрабатываются шейдеры в Qt Quick в версии Qt 5.14 при переключении графа сцены на рендеринг через QRhi и Qt Rendering Hardware Interface (аппаратный интерфейс рендеринга Qt). Охватим обработку шейдеров, прежде чем углубляться в сам RHI, потому что приложения Qt Quick, использующие элементы ShaderEffect или нестандартные материалы (custom materials), должны сами предоставлять код фрагмента и/или вершинного шейдера, и поэтому они должны знать (а в Qt 6 перейти на) новый подход к обработке шейдеров.

Говоря о Qt 6 (хотя все описанное здесь относится только к Qt 5.14 и может измениться в более поздних выпусках) то, что здесь имеется, скорее всего, послужит основой для обработки графики и вычисления шейдеров в Qt 6, как только несколько оставшихся неровных краев будут устранены.

Почему что-то новое?

Проблема 1

Просмотр дерева исходных текстов qt (т.е. git repo, содержащего QtQml, QtQuick и связанных с ним модулей) и поиск в каталоге шейдеров с вершинными и фрагментными шейдерами для встроенных материалов сценографа Qt Quick показывает, что Qt Quick уже поставляется с двумя версиями каждого вершинного или фрагментного шейдера GLSL:

Почему? Это связано с поддержкой контекстов OpenGL профиля ядра (для версии 3.2 и выше). Поскольку реализации OpenGL не обязаны поддерживать компиляцию шейдеров GLSL 100/110/120 в таком контексте, у Qt нет другого выбора, кроме как поставлять его с двумя вариантами: один подходит для OpenGL ES 2.0, OpenGL 2.1 и профилей совместимости, а другой (версии 150 на практике), который используется только тогда, когда контекст оказывается профильным. Как описано в части 1 серии статей, это важно для того, чтобы позволить разработчикам приложений решать, какой контекст OpenGL запрашивать, когда их приложение комбинирует свой собственный рендеринг OpenGL и пользовательский интерфейс на основе Qt Quick, независимо от того, является ли контекст совместимостью или контекстом основного профиля, Qt Quick по-прежнему сможет отображать.

Это нормально, когда число вариантов равно 2. Что если теперь могут понадобиться Vulkan-совместимые GLSL (Vulkan-compatible GLSL), HLSL и MSL в дополнение? К сожалению, подход не очень масштабный.

Проблема 2

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

Проблема 3

Внутренняя деталь: система пакетирования сценографа Qt Quick немного полагается на переписывание вершинного шейдера для материалов, которые используются с так называемой объединенной партией (merged batch) (это то, что получается, когда несколько геометрических узлов в конечном итоге генерируют один единственный вызов отрисовки). Перезапись шейдера на лету перед передачей его в glCompileShader подходит, когда используется только один язык шейдинга, но не масштабируется, когда нам нужно реализовать одну и ту же логику для нескольких разных языков.

Что тогда?

Глядя на страницу Khronos SPIR page, вы видите красивую и информативную картину SPIR-V Open Source Ecosystem (об экосистеме открытого источника SPIR-V). Почему бы не попробовать опираться на это?

Ключевые компоненты, которые интересны:

• glslang, компилятор из (OpenGL или Vulkan) GLSL в SPIR-V, промежуточное представление.

• SPIRV-Cross, библиотека для размышления о SPIR-V и разборки его на языки высокого уровня, такие как GLSL, HLSL и MSL.

Так что, если «стандартизировать» один язык, такой как GLSL в стиле Vulkan, скомпилировать его в SPIR-V, получится что-то подходящее для Vulkan. Если затем запустить двоичный файл SPIR-V через SPIRV-Cross, получим необходимую информацию об отражении и сможем сгенерировать исходный код для различных версий GLSL, HLSL и Metal Shading Language (GLSL по-прежнему важен, потому что, хотя для OpenGL существуют расширения, использующие SPIR-V, на это рассчитывать просто не получится, поскольку такое расширение, вероятно, будет отсутствовать на 90% целевых платформ и устройств Qt).

Наконец, соберите все это вместе (включая метаданные отражения) в легко (де)сериализуемый пакет, и там имеется нужное решение.

Поэтому конвейер, который используется при запуске приложения Qt Quick с установленным QSG_RHI=1 :

Vulkan-flavor GLSL

[ -> generate batching-friendly variant for vertex shaders]

-> glslang : SPIR-V bytecode

-> SPIRV-Cross : reflection metadata + GLSL/HLSL/MSL source

-> pack it all together and serialize to a .qsb file

Расширение .qsb происходит от имени инструмента командной строки, выполняющего описанные выше шаги - qsb , сокращение от Qt Shader Baker (не путать с QBS).

Во время выполнения файлы .qsb десериализуются в экземпляры QShader. Это довольно простой контейнер, следующий стандартным шаблонам Qt, таким как неявное совместное использование, и содержащий несколько вариантов исходного кода и байт-кода для одного шейдера, вместе с QShaderDescription, который, что неудивительно, содержит данные отражения. Как и остальная часть RHI, эти классы на данный момент являются частным API.

Графический слой напрямую использует экземпляры QShader. Объекты состояния графического конвейера задают QShader для каждой активной стадии шейдера. Затем бэкенды QRhi выбирают подходящий вариант шейдера из пакета QShader.

Начиная с версии Qt 5.14 на практике это означает поиск:

• SPIR-V 1.0 при нацеливании на Vulkan,
• Источник HLSL или DXBC для Shader Model 5.0, при нацеливании на D3D11,
• Metal 1.2 совместимый MSL-источник или предварительно скомпилированный metalib при нацеливании на Metal,
• источник GLSL для одной из версий 320, 310, 300, 100, при нацеливании на контекст OpenGL ES (в этом порядке приоритетов, начиная с самой высокой версии, поддерживаемой контекстом),
• источник GLSL для одной из версий 460, 450, ..., 330, 150, при нацеливании на контекст профиля ядра OpenGL (в том порядке приоритета, начиная с самой высокой версии, поддерживаемой контекстом),
• Источник GLSL для одной из версий 120, 110, при нацеливании на неосновной контекст OpenGL (в этом порядке приоритетов).

Поначалу записи HLSL и MSL в приведенном выше списке могут показаться немного странными. Это связано с тем, что можно компилировать HLSL и MSL из исходного кода во время выполнения (подход по умолчанию), также были проведены некоторые эксперименты, позволяющие включать предварительно скомпилированные промежуточные форматы в пакеты .qsb . На практике это означает вызов fxc (пока нет поддержки dxc - это тоже в планах, но будет действительно актуально только после того, как QT Company начнет смотреть на D3D12) или инструментов командной строки Metal перед этапом "-> pack it all together" («упаковать все вместе») в конвейер показан выше. Проблема здесь заключается в том, что такие инструменты связаны с их платформой (Windows и MacOS соответственно), поэтому qsb может вызывать их только при работе на этой платформе. Так, например, при ручной генерации файла .qsb в Linux это не вариант. В долгосрочной перспективе это, скорее всего, станет меньшей проблемой, потому что во временном интервале Qt 6 разработчикам все равно понадобится исследовать лучшую интеграцию системы сборки, поэтому ручные инструменты, такие как qsb, будут менее распространены.

Откуда эта штука qsb?

Из модуля Qt Shader Tools. Это обеспечивает как API, QShaderBaker, так и инструмент командной строки qsb для выполнения этапов компиляции, перевода и упаковки, описанных выше.

На данный момент модуль qt-labs не поставляется с Qt 5.14.

Почему? Ну, в основном из-за сторонних зависимостей, таких как glslang и SPIRV-Cross. Есть несколько вещей, которые нужно исследовать и выяснить, когда речь заходит о возможности компилирования и запуска на всех целевых платформах Qt Company, также некоторых вещей, связанных с лицензиями и т. д. Если все это звучит знакомо, то это потому, что некоторые из этих проблем были упомянуты в первой части этой серии статей, когда шла речь о решениях для перевода API. Итак, на данный момент создание пакета .qsb включает в себя проверку и сборку этого модуля, а затем запуск инструмента qsb вручную.

В то время, как необходимо решение, которое входит в комплект поставки Qt, полагаться на автономную обработку шейдеров не так уж плохо. В любом случае это одна из целей для Qt 6. Идея состоит в том, чтобы иметь что-то, что интегрируется с системой сборки Qt (Qt's build system) , чтобы описанные выше шаги обработки шейдера выполнялись во время сборки приложения (или библиотеки). Это остается, как будущее задание, главным образом из-за предстоящего перехода qmake -> cmake. Как только ситуация стабилизируется, можно начать строить решение поверх новой системы.

Так как же это делает Qt Quick в Qt 5.14?

Глядя на qtdeclarative/src/quick/scenegraph/shaders_ng, ответ очевиден, запустив qsb вручную (обратите внимание на compile.bat) и включив получившиеся файлы .qsb в библиотеку Qt Quick через систему ресурсов Qt. Это должно стать немного более сложным позднее.

Файлы .vert и .frag содержат совместимый с Vulkan код GLSL и не поставляются в сборке Qt Quick. В файле scenegraph.qrc перечислены только файлы .qsb.

Процесс хорошо описан на слайде ниже:

Каждый материал имеет только одну пару вершинных и фрагментных шейдеров, всегда записываемых как Vulkan-совместимый GLSL, следуя нескольким простым соглашениям (как, например, использование только одного унифицированного буфера, помещенного в привязку 0).

Каждый из этих файлов затем запускается через аппарат для «выпечки» шейдеров, в результате получается пакет QShader. В этом примере результатом является 6 версий одного и того же шейдера, а также данные отражения (которые qsb может печатать, как текст JSON, однако, сами файлы .qsb являются сжатыми двоичными файлами и не читаются человеком). Проблемы 1 и 2 , о которых говорилось выше, таким образом, решены.

Обратите внимание на теги [Standard] в списке шейдеров. Если бы это был вершинный шейдер, а также был указан аргумент -b, количество выходных шейдеров было бы 12, а не 6. 6 дополнительных были бы помечены, как [Batchable], указывая, что они были дружественными для пакетирования. Слегка измененные варианты, предназначенные для рендерера графа сцен Qt Quick. Это решает проблему 3 за счет несколько более высоких требований к хранению (но из-за сокращенного времени выполнения это, вероятно, того стоит).

Это охватывает основные концепции нового шейдерного конвейера. Мы должны взглянуть на ShaderEffect и QSGMaterial в отдельной статье. Основная идея (начиная с Qt 5.14) состоит в том, чтобы передавать имена файлов .qsb вместо исходных строк шейдеров, но, в частности, материалы должны знать еще несколько вещей (в основном из-за работы с унифицированными буферами вместо единичных униформ и из-за отсутствие концепции текущего контекста для каждого потока, где каждый может произвольно изменять состояния). Но обо всем этом в другой раз.

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.
Support the author Donate

Comments

Only authorized users can post comments.
Please, Log in or Sign up
Donate

Hello, Dear Users of EVILEG!!!

If the site helped you, then support the development of the site financially, please.

You can do it by following ways:

Thank you, Evgenii Legotckoi

O
Nov. 18, 2019, 3:54 p.m.
Oksana

C++ - Test 005. Structures and Classes

  • Result:66points,
  • Rating points-1
AG
Nov. 18, 2019, 3:50 p.m.
Anton Gur'ev

C++ - Test 001. The first program and data types

  • Result:46points,
  • Rating points-6
VZ
Nov. 17, 2019, 2:25 a.m.
Vladimir Zhitkovsky

Qt - Test 001. Signals and slots

  • Result:78points,
  • Rating points2
Last comments
c
Nov. 18, 2019, 6:27 a.m.
cyberaxe77

Моих знаний пока явно недостаточно, чтобы писать статьи. Так...небольшие заметки "на полях"))).
Nov. 18, 2019, 6:10 a.m.
Evgenij Legotskoj

Пока что на сайте нет активных пользователей PyQt5, кто бы мог писать статьи по PyQt5, к сожалению. Лично я только для статей пользуюсь этой библиотекой. Но можете стать одним из первых ;) Любой…
Nov. 18, 2019, 2:44 a.m.
Evgenij Legotskoj

Добрый день. На тот момент ничего не использовал дополнительно и никаких специальных настроек не делал. Просто собрал и получилось 10 мб.
c
Nov. 17, 2019, 2:25 p.m.
cyberaxe77

Шикарно! Как обычно всё объясняется чётко, ясно и, по-возможности, кратко. Побольше бы контента по PyQt5...
GV
Nov. 16, 2019, 4:16 p.m.
Gibraltar Vol'framov

Разрешите узнать, как вы получили qt+qml приложение размером в 10мб(даже релизных). Дело в том, что пустое приложение, хоть и со стек вью, обходится мне на все 40мб пространства. Или же вы воспо…
Now discuss on the forum
Nov. 19, 2019, 5:50 a.m.
Evgenij Legotskoj

Попробуй переписать метод так Q_INVOKABLE void createNewGroup(QObject *grp); А в реализации делать каст на нужный класс, ну для начала хотя бы просто посмотреть, прилетел ли в дебаг хоть…
Nov. 19, 2019, 5:04 a.m.
Evgenij Legotskoj

label - это GUI элемент в данном случае? Если так, то я бы не стал раскидывать GUI элементы в разные потоки. Дело в том, что в документации на Qt, сказано, что GUI элементы работают только в GUI…
MP
Nov. 19, 2019, 4:13 a.m.
Mikhail Petrov

Без разницы в дизайнере создается форма или не в дизайнере. Как вы добавляете в QTabWidget?
Nov. 19, 2019, 2:36 a.m.
BlinCT

Всем привет. Если кто дебажил qml часть подскажиет пожалуйста, какие настрйоки требуются в Qt Creator? Я собирал 5.12.5 из исходников, в Настройкс в debugger у меня выставлен путь к со…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB