mafulechka
mafulechka10 октября 2019 г. 5:03

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

В третьей части серии статей о графике 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 вместо исходных строк шейдеров, но, в частности, материалы должны знать еще несколько вещей (в основном из-за работы с унифицированными буферами вместо единичных униформ и из-за отсутствие концепции текущего контекста для каждого потока, где каждый может произвольно изменять состояния). Но обо всем этом в другой раз.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
г
  • ги
  • 24 апреля 2024 г. 1:51

C++ - Тест 005. Структуры и Классы

  • Результат:41баллов,
  • Очки рейтинга-8
l
  • laei
  • 23 апреля 2024 г. 19:19

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:10баллов,
  • Очки рейтинга-10
l
  • laei
  • 23 апреля 2024 г. 19:17

C++ - Тест 003. Условия и циклы

  • Результат:50баллов,
  • Очки рейтинга-4
Последние комментарии
k
kmssr9 февраля 2024 г. 5:43
Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко5 февраля 2024 г. 12:50
Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 декабря 2023 г. 21:30
Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 декабря 2023 г. 19:38
Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 декабря 2023 г. 8:01
Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Сейчас обсуждают на форуме
G
Gar22 апреля 2024 г. 15:46
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil Academics20 апреля 2024 г. 17:45
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasov14 апреля 2024 г. 16:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 апреля 2024 г. 12:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex4 апреля 2024 г. 14:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Следите за нами в социальных сетях