mafulechkaOct. 10, 2019, 5:03 a.m.

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

Content

В третьей части серии статей о графике 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

Did the EVILEG help you? Support the development of the site!

PayPalYandex.Money
How to become an author?

Contribute to the evolution of the EVILEG community.

Learn how to become a site author.

Learn it
Timeweb

Let me recommend you the excellent hosting on which EVILEG is located.

For many years, Timeweb has been proving his stability.

For projects on Django I recommend VDS hosting

View Hosting
SK

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

  • Result:80points,
  • Rating points4
SK

Qt - Test 001. Signals and slots

  • Result:78points,
  • Rating points2
S
  • Sergej
  • July 10, 2020, 3:15 p.m.

Qt - Test 001. Signals and slots

  • Result:68points,
  • Rating points-1
Last comments

Qt/C++ - Lesson 023. Moving QGraphicsItem on QGraphicsScene with mouse help

Пережиток plain C, ограничение видимости. По идее можно и .c, .cpp файлы подключать через директиву include. Для компилятора разницы особой нет, какое расширение будет.
R

Qt/C++ - Lesson 023. Moving QGraphicsItem on QGraphicsScene with mouse help

Подскажите, пожалуйста, почему функция рандома определена только в спп файле и объявлена при этом статической?
V

Django - Tutorial 027. Implementation Google reCAPTCHA

Спасибо. Только использую декоратор не в urls.py а перед views
R

Qt WinAPI - Lesson 001. How to collect all DLL, which used in Qt project?

Вы меня не совсем правильно поняли, но все равно спасибо, принял все к сведению. Все сделал как вы сказали, все отлично работает, еще раз огромнейшее спасибо) Разве что только что были опять про…

Qt WinAPI - Lesson 001. How to collect all DLL, which used in Qt project?

Стоило перед использованием что ли инструкцию прочитать https://www.cyberforum.ru/blogs/131347/blog2457.html "После сборки при запуске требовались dll," Ясное дело стоило задепло…
Now discuss on the forum
m

Qt IOs магнитное поле

Всем привет! Пытаюсь получить данные магнитного поля, используя класс QMagnetometer. Все получается, если задать параметр returnGeoValues false (как по-умолчанию). При этом выдаются ra…
DK

Drug Drop problems

благодорю!

Как в Qt в qmenu добавить scrollarea

Вот это наследованный класс меню. Но посути это обычное меню. #pragma once#include <QtWidgets>class TransMenu : public QMenu { Q_OBJECTpublic: TransMenu(QWidget* parent = …
o

Нужен человек кто хорошо понимает паттерны и их использование

Добрый день. Если вопрос про паттерны, то рисуй диаграмму классов.

Сборка Qt / C++ проекта под windows и linux

Отбой. Забыл в исходнике обернуть каждый #include макросом. #ifdef Q_OS_WIN32#include "win_controller.h"#else#include "linux_controller.h"#endif
About
Services
© EVILEG 2015-2020
Recommend hosting TIMEWEB