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

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

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

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

v
April 9, 2020, 5:41 a.m.
vitalisimys

Qt - Test 001. Signals and slots

  • Result:21points,
  • Rating points-10
v
April 9, 2020, 5:36 a.m.
vitalisimys

C ++ - Test 004. Pointers, Arrays and Loops

  • Result:40points,
  • Rating points-8
v
April 9, 2020, 5:32 a.m.
vitalisimys

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

  • Result:53points,
  • Rating points-4
Last comments
April 8, 2020, 9:12 a.m.
ogustbiller

Круто! Немного начинает проясняться что к чему. Спасибо.
K
April 7, 2020, 8:55 a.m.
KULINAR847

А вот уже и на python... #!/usr/bin/env python# -'''- coding: utf-8 -'''-import sysfrom PySide2.QtWidgets import *from PySide2.QtQuick import *from PySide2.QtCore import *from PySide2.…
April 3, 2020, 8:06 a.m.
Konstantin Grudnitskiy

Я надеюсь вы уже разобрались в чем дело, но если вдруг нет, то проблема состоит в том, что вы пытаетесь запустить программу из интерпретатора питона. Файл main.py это уже готова…
April 3, 2020, 6:18 a.m.
Konstantin Grudnitskiy

>>> text = 'hello world'>>> ' '.join(word for word in text.split()[:-1])'hello'>>> def remove_last_word(text):... return text and ' '.join(word for word in text.s…
March 27, 2020, 2:40 p.m.
Evgenij Legotskoj

Добрый день. В конце пятой статьи скачать можете.
Now discuss on the forum
DK
April 9, 2020, 3:31 a.m.
Dzhon Kofi

Привет. Делаю реализацию перемещения строк на QTableView с моделью QSqlTableModel. Буду в этой теме спрашивать нужное. Пока такой вопрос при создании модели: как мне узнать rowCount? int…
April 8, 2020, 5:42 p.m.
elyana

Подскажите как правильно реализовать... Нужно создать массив в C++ с переменными типа сhar или же int. В Qml будет переменная, например text, которая будет изменять своё значение в зависим…
DK
April 8, 2020, 6:29 a.m.
Dzhon Kofi

:-D блин, без слов, пойду пройдусь))
s
April 8, 2020, 4:59 a.m.
slava_d2000

Всем привет. Прошу помощи.есть статическая сборка 5.14.2 (без SSL) компилируется чистый шаблон на QML и Widgets если войти на компьютер в терминальной сессии и запустить программ…
s
April 6, 2020, 8:06 a.m.
shuric

Добрый день. Объясните пожалуйста ... ... допиливать стилевое оформление в прокси классе ... где именно копать ? В каком виртуальном методе лучше допиливать (если можно н…
EVILEG
About
Services
© EVILEG 2015-2019
Recommend hosting TIMEWEB