Адаптация Qt Quick Direct3D 12
Адаптация Direct3D 12 для Windows 10, как в Win32 (плагин платформы windows), так и в UWP (плагин платформы winrt), поставляется как динамически загружаемый плагин. Эта адаптация не работает на более ранних версиях Windows. Компиляция этого плагина включается автоматически, когда присутствуют необходимые файлы разработки D3D и DXGI. На практике это в настоящее время означает Visual Studio 2015 и новее.
Адаптация доступна как в обычных Qt-сборках с поддержкой OpenGL, так и при конфигурации Qt с -no-opengl. Однако она никогда не является стандартной, то есть пользователь или приложение должны явно запросить её, установив переменную среды QT_QUICK_BACKEND в значение d3d12 или вызвав QQuickWindow::setSceneGraphBackend().
Мотивация
Эта экспериментальная адаптация — первый бэкенд Qt Quick, который фокусируется на современной, низкоуровневой графической API в сочетании с интерфейсом системы окон, отличным от традиционных подходов, используемых с OpenGL.
Эта адаптация также позволяет улучшить интеграцию с Windows, так как Direct3D является основной поддерживаемой поставщиком решением. Вследствие этого ожидается меньше проблем с драйверами, операциями, такими как изменение размеров окна, и особыми событиями, такими как потеря графического устройства, вызванная перезагрузкой устройства или обновлением графического драйвера.
С точки зрения производительности, ожидается несколько меньшая загрузка ЦП по сравнению с OpenGL из-за меньшей нагрузки на драйвер и более высокая загрузка видеокарты с меньшей потерей времени простоя. Бэкенд пока не сильно использует потоки, что означает возможности для дальнейших улучшений в будущем, например, для дальнейшей оптимизации загрузки изображений.
Бэкенд D3D12 также добавляет поддержку предварительно скомпилированных шейдеров. Все собственные шейдеры бэкенда (используемые встроенными материалами, на основе которых строятся типы Rectangle, Image, Text и другие типы QML) компилируются в байткод шейдеров D3D при компиляции Qt. Приложения, использующие элементы ShaderEffect, могут выбрать доставку байткода либо в обычных файлах, через систему ресурсов Qt, или использовать строки исходного кода языка высокоуровневого шейдинга для DirectX (HLSL). В отличие от OpenGL, компиляция HLSL корректно реализована с использованием нескольких потоков, что означает, что компиляция шейдеров не будет блокировать приложение и его пользовательский интерфейс.
Графические адаптеры
Плагин не обязательно требует аппаратного ускорения. Также можно использовать WARP, программный растризатор Direct3D. По умолчанию выбирается первый адаптер, предоставляющий аппаратное ускорение. Чтобы переопределить это и использовать другой графический адаптер или принудительно использовать программный растризатор, установите переменную среды QT_D3D_ADAPTER_INDEX в индекс адаптера. Обнаруженные адаптеры выводятся при запуске, если включена категория регистрации QSG_INFO или qt.scenegraph.general.
Отладка
Если у вас возникнут проблемы, всегда устанавливайте переменные среды QSG_INFO и QT_D3D_DEBUG в значение 1, чтобы получить сообщения об ошибках и предупреждениях, выводимые в вывод отладки. QT_D3D_DEBUG включает отладочный уровень Direct3D.
Примечание: Отладочный уровень не следует включать в рабочей среде, поскольку он может значительно повлиять на производительность (загрузку ЦП) из-за увеличенной нагрузки на API.
Циклы отрисовки
По умолчанию адаптация D3D12 использует однопоточный цикл отрисовки, аналогичный циклу отрисовки windows OpenGL. Также доступна многопотоковая версия, которую можно запросить, установив переменную среды QSG_RENDER_LOOP в значение threaded. Однако из-за концептуальных ограничений в DXGI, интерфейсе системы окон, многопоточный цикл подвержен тупиковым ситуациям при отображении нескольких экземпляров QQuickWindow или QQuickView. Вследствие этого, пока что используется однопоточный цикл по умолчанию. Это означает, что с бэкендом D3D12 приложениям следует переместить свою работу из основного потока (GUI) в рабочие потоки, вместо ожидания от Qt поддержания отзывчивости GUI-потока и его пригодности для сложных блокирующих операций.
Для получения дополнительной информации см. Графический сценарий Qt Quick для получения подробностей о циклах отрисовки и Многопоточность и DXGI по проблемам с многопоточностью.
Рендерер
Рендерер сцены в адаптации D3D12 в настоящее время не выполняет никакой кэширования. Это менее важно, чем в OpenGL, потому что изменения состояния не представляют проблем в первую очередь. Более простая логика рендеринга также может в некоторых случаях привести к меньшей нагрузке на ЦП. В настоящее время проводятся исследования компромиссов между различными подходами.
Эффекты шейдеров
Тип QML ShaderEffect полностью функционален с адаптацией D3D12. Однако интерпретация свойств fragmentShader и vertexShader отличается от OpenGL.
В D3D12 эти строки могут быть URL-адресом локального файла, файла в системе ресурсов или строкой исходного кода HLSL. Использование URL-адреса для локального файла или файла в системе ресурсов указывает на то, что соответствующий файл содержит предварительно скомпилированный байткод шейдера D3D, сгенерированный инструментом fxc, или, как альтернатива, исходный код HLSL. Тип файла определяется автоматически. Это означает, что бэкенд D3D12 поддерживает все варианты из GraphicsInfo.shaderCompilationType и GraphicsInfo.shaderSourceType.
В отличие от OpenGL, при открытии файла используется QFileSelector с дополнительным селектором hlsl. Это обеспечивает простое создание элементов ShaderEffect, которые работают в обоих бэкендах, например, поместив исходный код GLSL в shaders/effect.frag, исходный код HLSL или — предпочтительно — предварительно скомпилированный байткод в shaders/+hlsl/effect.frag, в то время как в QML просто пишется fragmentShader: "qrc:shaders/effect.frag". Для получения более подробной информации см. ShaderEffect.
Многообразные целевые области рендеринга
Адаптация Direct3D 12 игнорирует установленное в QSurfaceFormat QQuickWindow или QQuickView или установленное с помощью QSurfaceFormat::setDefaultFormat() значения, за исключением двух случаев: QSurfaceFormat::samples() и QSurfaceFormat::alphaBufferSize() все еще учитываются. Если значение выборки больше 1, целевые области рендеринга с выборкой вне экрана будут созданы с указанным количеством выборки на максимальном поддерживаемом уровне качества. Бэкенд автоматически выполняет разрешение в буферы обмена без множественной выборки после каждого кадра.
Полупрозрачные окна
Когда канал альфа включен с помощью QQuickWindow::setDefaultAlphaBuffer() или путем установки значения alphaBufferSize отличного от нуля в QSurfaceFormat окна или в глобальном формате, управляемом QSurfaceFormat::setDefaultFormat(), бэкенд D3D12 создаст цепочку обмена для композиции и пройдет через DirectComposition. Это необходимо, потому что обязательная цепочка обмена flip-модель иначе не поддерживает прозрачность.
Поэтому не следует излишне запрашивать канал альфа. Когда alphaBufferSize равен 0 или по умолчанию -1, все эти дополнительные шаги можно избежать, и традиционная цепочка обмена на основе окна будет достаточной.
В WinRT это не актуально, потому что бэкенд всегда использует цепочку обмена композиции, связанную с ISwapChainPanel, которая поддерживает QWindow на этой платформе.
Mip-карты
Генерация mip-карт поддерживается и обрабатывается прозрачно для приложений с помощью встроенного вычислительного шейдера. Однако в настоящее время эта функция находится в стадии разработки и поддерживает только изображения, являющиеся степенью двойки. Текстуры других размеров тоже будут работать, но это предполагает масштабирование на основе QImage на ЦП в первую очередь. Поэтому избегайте включения mip-карт для изображений не являющихся степенью двойки (NPOT), когда это возможно.
Форматы изображений
При создании текстур с помощью API сцены C++, таких как QQuickWindow::createTextureFromImage(), 32-битные форматы не будут подвергаться преобразованию, они будут отображаться непосредственно в соответствующий формат R8G8B8A8_UNORM или B8G8R8A8_UNORM. Всё остальное вызовет преобразование формата на основе QImage на ЦП в первую очередь.
Неподдерживаемые функции
Частицы и некоторые другие утилиты, зависящие от OpenGL, такие как QQuickFramebufferObject, в настоящее время не поддерживаются.
Как и с адаптацией программного обеспечения, текст всегда отображается с использованием родного метода. Отображение текста на основе областей расстояния в настоящее время не реализовано.
Исходные коды шейдеров в модуле Qt Graphical Effects не были перенесены в какой-либо формат, совместимый с OpenGL 2.0, что означает, что типы QML, предоставляемые этим модулем, в настоящее время не работают с бэкендом D3D12.
Текстурные атласы в настоящее время не используются.
Рендерер может не поддерживать определённые мелкие функции, такие как рисование точек и линий с шириной, отличной от 1.
Пользовательские элементы Qt Quick, использующие пользовательские узлы графика сцены, могут представлять собой проблему, поскольку материалы изначально связаны с графическим API. Поэтому функциональны только элементы, использующие служебные узлы прямоугольника и изображения, в пределах всех адаптаций.
QQuickWidget и его лежащая в основе архитектура композиции на основе OpenGL не поддерживаются. Если вам нужно объединить с интерфейсами пользователя на основе QWidget, используйте QWidget::createWindowContainer(), чтобы встроить родное окно QQuickWindow или QQuickView.
Наконец, рендеринг через QSGEngine и QSGAbstractRenderer в настоящее время невозможен с адаптацией D3D12.
Связанные API
Для интеграции пользовательского рендеринга Direct3D 12 используйте QSGRenderNode в сочетании с QSGRendererInterface. Этот подход не зависит от контекстов OpenGL или специфики API, таких как буферы кадров, и позволяет раскрыть графическое устройство и буфер команд из адаптации. Он не обязательно подходит для лёгкой интеграции всех типов содержимого, в частности, для истинного 3D, поэтому, вероятно, в будущих выпусках его дополнит альтернатива QQuickFramebufferObject.
Для принятия решений во время выполнения на основе адаптации используйте QSGRendererInterface из C++ и GraphicsInfo из QML. Они также могут использоваться для проверки уровня поддержки шейдеров: языка шейдеров, подхода к компиляции и т.д.
END_OF_DOCUMENT_MARKERПри создании пользовательских элементов используйте новые классы QSGRectangleNode и QSGImageNode. Они заменяют устаревшие QSGSimpleRectNode и QSGSimpleTextureNode. В отличие от своих предшественников, эти новые классы являются интерфейсами, а реализации создаются с помощью функций-фабрик QQuickWindow::createRectangleNode() и QQuickWindow::createImageNode().
Расширенная настройка
Адаптация D3D12 может поддерживать несколько кадров в процессе обработки, подобно современным игровым движкам. Это несколько отличается от традиционной модели "рендер - обмен - ожидание vsync" и позволяет лучше использовать GPU за счёт большего потребления ресурсов. Это означает, что рендерер будет на несколько кадров опережать то, что отображается на экране.
Для обсуждения моделей обмена цепочек смены кадров и типичных параметров конфигурации см. Образец приложения для Direct3D 12 Flip Model Swap Chains.
Вертикальная синхронизация всегда включена, что означает, что Present() вызывается с интервалом 1.
Конфигурацию можно изменить, установив следующие переменные среды:
| Переменная среды | Описание |
|---|---|
QT_D3D_BUFFER_COUNT |
Количество буферов цепочки обмена кадров в диапазоне 2 - 4. Значение по умолчанию - 3. |
QT_D3D_FRAME_COUNT |
Количество кадров, подготовленных без блокировки, в диапазоне 1 - 4. Значение по умолчанию - 2. Present() начинает блокировать после очереди 3 кадров (независимо от QT_D3D_BUFFER_COUNT), если ожидаемый объект не используется. Каждый дополнительный кадр увеличивает использование ресурсов GPU, так как геометрия и данные константного буфера должны дублироваться, и это связано с большей обработкой со стороны процессора. |
QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY |
Задержка кадра в диапазоне 1 - 16. Значение по умолчанию - 0 (выключено). Изменяет предел для Present() и вызывает ожидание доступного буфера цепочки обмена кадров при начале каждого кадра. Для подробного обсуждения см. статью, указанную выше. Примечание: В настоящее время это поведение является экспериментальным. |
QT_D3D_BLOCKING_PRESENT |
Время, которое процессор должен ждать, ненулевое значение, для завершения работы графического процессора после каждого вызова Present(). Значение по умолчанию - 0 (выключено). Это поведение фактически убивает всю параллельность, но делает поведение похожим на традиционную модель swap-blocks-for-vsync, что может быть полезно в некоторых особых случаях. Однако это поведение не эквивалентно установке количества кадров в 1, поскольку это всё равно избегает блокировки после Present(), и может блокировать только при начале подготовки следующего кадра (или вообще не блокировать в зависимости от временного интервала между кадрами). |
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.14/qtquick-visualcanvas-adaptations-d3d12.html