Адаптация 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, в то время как просто пишем fragmentShader: "qrc:shaders/effect.frag" в QML. Более подробную информацию см. в ShaderEffect.
Многообразные цели рендеринга
Адаптация Direct3D 12 игнорирует значения QSurfaceFormat, установленные в QQuickWindow или QQuickView, или установленные с помощью QSurfaceFormat::setDefaultFormat() с двумя исключениями: QSurfaceFormat::samples() и QSurfaceFormat::alphaBufferSize() по-прежнему учитываются. Когда значение образца больше 1, будут созданы многообразные буферы рендеринга вне экрана со указанным количеством образцов на максимальном поддерживаемом уровне качества. Бэкенд автоматически выполняет разрешение в буферы обмена без многообразия после каждого кадра.
Полупрозрачные окна
При включении альфа-канала с помощью QQuickWindow::setDefaultAlphaBuffer() или путем задания ненулевого значения alphaBufferSize в QSurfaceFormat окна или в глобальном формате, управляемом QSurfaceFormat::setDefaultFormat(), бэкенд D3D12 создаст цепочку обмена для композиции и пройдет через DirectComposition. Это необходимо, так как обязательная цепочка обмена с переворачиванием не поддерживает прозрачность в противном случае.
Поэтому важно не запрашивать ненужный альфа-канал. Когда alphaBufferSize равен 0 или значению по умолчанию -1, все эти дополнительные шаги могут быть избегнуты, и традиционная цепочка обмена на основе окна является достаточной.
В WinRT это не актуально, поскольку бэкенд всегда использует цепочку обмена композиции, которая связана с 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 в будущих версиях.
Для выполнения решений в реальном времени, основанных на адаптации, используйте QSGRendererInterface на C++ и GraphicsInfo в QML. Они также могут использоваться для проверки уровня поддержки шейдеров: языка шейдеров, подхода к компиляции и т. д.
END_OF_DOCUMENT_MARKERПри создании пользовательских элементов используйте новые классы QSGRectangleNode и QSGImageNode. Они заменяют устаревшие QSGSimpleRectNode и QSGSimpleTextureNode. В отличие от своих предшественников, эти новые классы являются интерфейсами, а реализации создаются с помощью функций-фабрик QQuickWindow::createRectangleNode() и QQuickWindow::createImageNode().
Расширенная настройка
Адаптация D3D12 может поддерживать несколько кадров в процессе обработки, подобно современным игровым движкам. Это несколько отличается от традиционной модели «рендеринг — обмен — ожидание vsync» и позволяет улучшить использование графического процессора за счёт увеличения использования ресурсов. Это означает, что рендерер будет на несколько кадров опережать отображаемое на экране.
Для обсуждения моделей обмена буферами flip и типичных параметров конфигурации обратитесь к Приложению-примеру для цепочек обмена буферами Direct3D 12 с моделью flip.
Вертикальная синхронизация всегда включена, что означает, что Present() вызывается с интервалом 1.
Конфигурацию можно изменить, установив следующие переменные среды:
| Переменная среды | Описание |
|---|---|
QT_D3D_BUFFER_COUNT |
Количество буферов цепочки обмена в диапазоне 2—4. Значение по умолчанию — 3. |
QT_D3D_FRAME_COUNT |
Количество кадров, подготовленных без блокировки, в диапазоне 1—4. Значение по умолчанию — 2. Present() начинает блокировку после очереди 3 кадров (независимо от QT_D3D_BUFFER_COUNT), если объект ожидания не используется. Каждый дополнительный кадр увеличивает использование ресурсов графического процессора, так как данные геометрии и константного буфера необходимо дублировать, и это связано с большим объёмом работы со стороны ЦП. |
QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY |
Задержка кадра в диапазоне 1—16. Значение по умолчанию — 0 (выключено). Изменяет лимит для Present() и вызывает ожидание доступного буфера цепочки обмена при начале каждого кадра. Для подробного обсуждения см. статью по ссылке выше. Примечание: В настоящее время это поведение является экспериментальным. |
QT_D3D_BLOCKING_PRESENT |
Время, которое ЦП должен ждать, ненулевое значение, чтобы графический процессор завершил свою работу после каждого вызова Present(). Значение по умолчанию — 0 (выключено). Это поведение фактически убивает всю параллельность, но делает поведение похожим на традиционную модель «обмен — блокировка — vsync», что может быть полезно в некоторых особых случаях. Однако это поведение не эквивалентно установке количества кадров в 1, так как в этом случае всё равно избегается блокировка после Present(), и блокировка может произойти только при подготовке следующего кадра (или вообще не произойти, в зависимости от временного интервала между кадрами). |
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.15/qtquick-visualcanvas-adaptations-d3d12.html