Адаптация 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, либо использовать строки исходного кода High Level Shading Language для 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 использует однопоточный цикл отрисовки, аналогичный циклу отрисовки OpenGL windows. Также доступен многопоточный вариант, который можно запросить, установив переменную среды 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 будут созданы многообразные буферы offscreen рендеринга с указанным количеством выборки на максимальном поддерживаемом уровне качества. Бэкенд автоматически выполняет разрешение в буферы swapchain без многообразия после каждого кадра.
Полупрозрачные окна
Когда канал альфа-канала включен либо через QQuickWindow::setDefaultAlphaBuffer(), либо путем установки alphaBufferSize в ненулевое значение в QSurfaceFormat окна или в глобальном формате, управляемом QSurfaceFormat::setDefaultFormat(), бэкенд D3D12 создаст swapchain для композиции и пройдёт через DirectComposition. Это необходимо, потому что обязательный обмен данными с flip model swapchain не поддерживает прозрачность в противном случае.
Поэтому важно не запрашивать ненужный канал альфа-канала. Когда alphaBufferSize равен 0 или по умолчанию -1, все эти дополнительные шаги можно избежать, и традиционный swapchain на основе окон является достаточным.
В WinRT это не актуально, потому что бэкенд там всегда использует swapchain композиции, связанный с ISwapChainPanel, на котором основан QWindow на этой платформе.
Mipmaps
Генерация mipmap поддерживается и обрабатывается прозрачно для приложений с помощью встроенного вычислительного шейдера. Однако в данный момент эта функция экспериментальная и поддерживает только изображения, являющиеся степенями двойки. Текстуры других размеров тоже будут работать, но это предполагает масштабирование на основе QImage на ЦП в первую очередь. Поэтому избегайте включения mipmapping для изображений Non-Power-Of-Two (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.
END_OF_DOCUMENT_MARKERДля выполнения решений во время выполнения на основе адаптации используйте QSGRendererInterface из C++ и GraphicsInfo из QML. Их также можно использовать для проверки уровня поддержки шейдеров: язык шейдеров, подход к компиляции и так далее.
При создании пользовательских элементов используйте новые классы 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 |
Время ожидания процессора, ненулевое значение, пока GPU не завершит свою работу после каждого вызова 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.13/qtquick-visualcanvas-adaptations-d3d12.html