Теперь, когда приближается первая бета-версия Qt 5.14, пришло время поговорить об одной из самых важных новых функций. Сложно охватить все детали, касающиеся улучшений графического стека и пути к Qt 6 в одной статье, поэтому в частях 1 и 2 будет описан фон и более подробно рассмотрено, что будет поставляться с версией 5.14. Позже, в другой серии статей, рассмотрим технические детали и будущие направления.
На странице новых функций 5.14 упоминается: добавлен первый предварительный просмотр независимого от графического API рендера сценографа в качестве дополнительной функции. Это позволяет запускать соответствующие приложения Qt Quick поверх Vulkan, Metal или Direct3D 11 вместо OpenGL .
Что это значит на практике?
Одна из главных целей в Qt 6 - отойти от прямого использования OpenGL в большинстве мест в Qt и, при наличии соответствующих абстракций, позволить работать с более широким разнообразием графики API, такой, как Vulkan, Metal и Direct3D. Естественно, OpenGL (и OpenGL ES) остается опцией. Основной мотивацией этого является не повышение производительности, а обеспечение Qt Everywhere и в будущем, даже на платформах и устройствах, где OpenGL либо недоступен, либо нежелателен. В то же время, возможность использовать современные низкоуровневые API-интерфейсы также может открыть возможности, когда речь идет об улучшении производительности (например, при более низком использовании ЦП (CPU) из-за меньших накладных расходов API) и новых способах работы в движках рендеринга за Qt Quick и другими модулями, как недавно анонсированный Qt Quick 3D.
Кроме того, возможность отображать пользовательские интерфейсы с помощью основной платформы наиболее поддерживаемого графического API является отличной новостью для приложений, которые выполняют собственный родной 2D или 3D графический рендеринг при использовании Qt для визуализации пользовательского интерфейса. В таком случае Qt часто находится не «за рулем», когда дело доходит до решения, какой графический API использовать. Например, если настольное приложение на macOS хочет использовать Metal для своего собственного 3D-контента, полагаясь на Qt Quick для рендеринга 2D элементов пользовательского интерфейса, тогда очень полезно, если Qt Quick также рендерит через Metal. Это будет звучать знакомо для тех, кто следил за развитием графики в Qt 5.x. Концептуально это ничем не отличается от того, когда поддержка работы в контекстах профилей ядра OpenGL была представлена в рендерере Qt Quick. Сам по себе Qt Quick в этом не нуждается, но для того, чтобы позволить интегрировать внешний код рендеринга, связанный с основными функциями профиля, Qt Quick должен быть осведомлен и способен с ним справиться. Так что в этом смысле история является естественным продолжением того, что имелось в Qt 5, и теперь расширяется, чтобы охватить графические API, отличные от OpenGL.
Все сказанное выше может вызвать два очевидных вопроса:
• Как это относится к Qt 5.x? Разве это не весь материал Qt 6?
• Почему бы просто не использовать
<имя какого-либо решения для перевода графического API> (
Так что там в Qt 5.14?
Развертывание полного пересмотра графических битов во всех (или, по крайней мере, в большинстве) мест в Qt, действительно, предназначено для Qt 6. Однако, остановка работы над 5.x и попытка изобрести, разработать и реорганизовывать все за один раз, надеясь, что все будет хорошо, не очень привлекательна на практике. Как говорили (и продолжают говорить) разработчики Qt, первая итерация любого API, вероятно, будет неоптимальной. Потому вместо этого разработчики Qt используют разработанный параллельный подход , сосредоточив внимание на одной определенной технологии пользовательского интерфейса в Qt - Qt Quick.
Ожидается, что Qt 5.14 будет поставляться с предварительным просмотром нового пути рендеринга Qt Quick. По умолчанию это неактивно и, поэтому для приложений нет видимых изменений, внутри они проходят тот же прямой путь кода на основе OpenGL, что и в более ранних версиях. Тем не менее, те, кто хочет опробовать новый подход, могут подписаться: либо установив переменную среды, либо запросив ее через C ++ API в main ().
Взглянув на снимок документации Qt 5.14, обнаружим следующее:
Не все приложения будут работать из коробки при работе с установленным QSG_RHI. Пользовательские реализации QQuickItem с узлами графа сцены, выполняющими прямые вызовы OpenGL или содержащими код шейдера GLSL в пользовательских материалах не будут работать при включении рендеринга на основе RHI. То же самое относится к элементам ShaderEffect с исходным кодом GLSL. Решения для создания нестандартных материалов и эффектов современным способом, в основном, уже есть, но они требуют соответствующей миграции приложений. Ранние пользователи могут экспериментировать с этим уже в 5.14 и 5.15, но широкое принятие и миграция, естественно, не ожидается до Qt 6.0. С другой стороны, многие существующие приложения QML, скорее всего будут работать, даже, если базовый движок рендеринга будет проходить через совершенно другой API, такие, как Vulkan или Metal.
Почему бы не перевод слоя XYZ?
Прежде всего, важно отметить, что возможность использования API и слоев трансляции шейдеров таких, как MoltenVK, MoltenGL, ANGLE, Zink и других, все еще существует даже, если они не всегда доступны из коробки. Например, MoltenVK позволяет также отображать пользовательские интерфейсы Qt Quick через Vulkan на macOS. Если приложение Qt Quick желает использовать только Vulkan и все еще хочет работать на macOS, MoltenVK - вариант (до тех пор, пока надлежащим образом сконфигурированная сборка Qt используется развернутой, MoltenVK доступен в системах пользователей и т. д.).
Сделать такой слой перевода обязательной зависимостью, а значит включить и развернуть его с помощью Qt уже совсем другая история.
Излишне говорить, что изменение Qt Everywhere на Qt Only, где внешние зависимости Allow (Qt Only Where External Dependencies Allow) не является идеальным.
Qt нацелен большее количество платформ и сред, чем обычно думают. Он может полагаться только на обязательные сторонние зависимости, которые компилируются и работают в «экзотических» средах, и их легко адаптировать в случае возникновения особых потребностей (например, INTEGRITY, QNX, специализированные встроенные среды Linux, системы с неработающими или находящимися в разработке графическими стеками, редкая необходимость адаптации к частным частям, или, иногда, необходимость взаимодействия, возможно, в зависимости от поставщика, нестандартным способом с различными графическими или композиционными API, которые вы считали давно мертвыми, и т. д. Все это требует гибкости и настраиваемости на каждом уровне стека рендеринга Qt).
Выполнять перевод между языками затенения (или промежуточными форматами) во время выполнения не совсем идеально. Ожидается, что шейдерный конвейер в Qt 6 будет больше фокусироваться на работе в автономном режиме или, самое позднее, во время сборки приложения. Когда слой перевода находится посередине, скрывая реальность (какой API, какой язык используется на самом деле), эти усилия быстро становятся бесполезными на практике, поскольку нет возможности подготовить или ввести шейдеры, или байт-код для реального базового API.
Некоторые из упомянутых вариантов не подлежат обсуждению в связи с текущим состоянием реальности: OpenGL (ES) является и будет основной рабочей лошадкой в обозримом будущем на многих устройствах. Поэтому прямое использование одного API может означать, что API является OpenGL или уровнем перевода, который может быть нацелен на OpenGL (и также достаточно эффективен для малоприозводительных устройств).
Случаи, когда движки рендеринга Qt дополняются пользовательским, собственным кодом рендеринга, как правило, работают лучше всего, когда обе стороны используют одни и те же API напрямую. Использование слоя перевода не всегда является блокирующим в этом отношении, если они позволяют получить доступ к базовым нативным объектам (представьте, например, как Direct3D - Qt Quick взаимодействие стало возможным благодаря расширениям EGL при работе с Qt 5 поверх ANGLE в Windows) и, когда это так, это может создать еще одну проблему, с которой придется столкнуться.
Есть лицензионные последствия и проблемы. Вспомните, что Apache 2.0 несовместим с GPLv2. Полагаться на коммерческие решения в любом случае не может быть и речи.
Исходя из опыта (некоторые с ANGLE и немного с MoltenVK), использование таких решений никогда не было таким уж простым, как хотелось бы изначально. В какой-то момент усилия, необходимые для поддержания всех опций в рабочем состоянии, могут стать слишком большими, тогда эти усилия лучше потратить на то, чтобы сделать все «правильно» напрямую с помощью нативного API. Внутренне зависимая от платформы природа некоторых из этих решений по переводу также не идеальна, если нужно использовать другую для каждой целевой платформы Qt, то ситуация быстро становится несостоятельной.
Таким образом, вместо того, чтобы полагаться на низкоуровневые API-трансляторы, Qt определяет свою собственную высокоуровневую абстракцию для 3D-графики (для внутреннего использования, на данный момент, не доступной для приложений). Затем, это поддерживается специфичными для API реализациями бэкенда, шаблоном, знакомым по многим компонентам в Qt. В некоторых случаях бэкенд является платформо-зависимым по своей природе (Metal, D3D), в то время, как в некоторых других один бэкэнд предназначен для одного API, но нескольких платформ (Vulkan, OpenGL). Это дополняется новым конвейером управления шейдерами, основанным на нескольких сторонних проектах, таких, как glslang и SPIRV-Cross. Более подробно обо всем этом будет рассказано в следующих статьях.
Это действительно работает?
Давайте рассмотрим пример, а именно хорошо известное демонстрационное приложение Qt5 Cinematic Experience от QUIt Coding. Мы используем слегка измененную версию, в которой обновляются несколько элементов ShaderEffect, чтобы они работали с обоими путями рендеринга сценографа Qt Quick.
При нормальном запуске приложения с установленным QSG_INFO=1 получаем:
Как показывают журналы, напечатанные в результатах отладки, это работает в OpenGL на рабочем столе Linux:
qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms qt.scenegraph.general: opengl texture atlas dimensions: 2048x1024 qt.scenegraph.general: GL_VENDOR: X.Org qt.scenegraph.general: GL_RENDERER: AMD Radeon (TM) R9 M360 (VERDE, DRM 3.23.0, 4.15.0-62-generic, LLVM 8.0.1) qt.scenegraph.general: GL_VERSION: 4.5 (Compatibility Profile) Mesa 19.2.0-devel (git-08f1cef 2019-07-25 bionic-oibaf-ppa) qt.scenegraph.general: GL_EXTENSIONS: ... qt.scenegraph.general: Max Texture Size: 16384 qt.scenegraph.general: Debug context: false
Как это изменится, если мы установим QSG_RHI=1?
qt.scenegraph.general: Using QRhi with backend OpenGL graphics API debug/validation layers: 0 QRhi profiling and debug markers: 0 qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms qt.rhi.general: Created OpenGL context QSurfaceFormat(version 4.5, options QFlags<QSurfaceFormat::FormatOption>(DeprecatedFunctions), depthBufferSize 24, redBufferSize 8, greenBufferSize 8, blueBufferSize 8, alphaBufferSize 0, stencilBufferSize 8, samples -1, swapBehavior QSurfaceFormat::DoubleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile QSurfaceFormat::CompatibilityProfile) qt.rhi.general: OpenGL VENDOR: X.Org RENDERER: AMD Radeon (TM) R9 M360 (VERDE, DRM 3.23.0, 4.15.0-62-generic, LLVM 8.0.1) VERSION: 4.5 (Compatibility Profile) Mesa 19.2.0-devel (git-08f1cef 2019-07-25 bionic-oibaf-ppa) qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no. qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024
Не сильно отличается, на первый взгляд. Все еще кажется, что это происходит через OpenGL. Тем не менее, внутренне нет прямого использования OpenGL и больше нет шейдерных источников GLSL в сцене Qt Quick. Вместо этого рендеринг проходит через QRhi, аппаратный интерфейс рендеринга Qt (частный API в модуле QtGui на данный момент).
Теперь давайте установим QSG_RHI_BACKEND=vulkan :
qt.scenegraph.general: Using QRhi with backend Vulkan graphics API debug/validation layers: 0 QRhi profiling and debug markers: 0 qt.scenegraph.general: threaded render loop qt.scenegraph.general: Using sg animation driver qt.scenegraph.general: Animation Driver: using vsync: 16.95 ms WARNING: radv is not a conformant vulkan implementation, testing use only. qt.rhi.general: Physical device 0: 'AMD RADV CAPE VERDE (LLVM 8.0.1)' 19.1.99 qt.rhi.general: using this physical device qt.rhi.general: queue family 0: flags=0xf count=1 qt.rhi.general: queue family 1: flags=0xe count=2 qt.rhi.general: 55 device extensions available qt.scenegraph.general: MSAA sample count for the swapchain is 1. Alpha channel requested = no. qt.scenegraph.general: rhi texture atlas dimensions: 2048x1024 qt.rhi.general: Creating new swapchain of 3 buffers, size 1280x720, presentation mode 2
По всей видимости теперь рендеринг происходит через Vulkan. Тем не менее, даже самые экзотические функции Qt Quick, такие, как рендеринг текста в дистанционном поле, эффекты шейдеров и частицы, присутствуют, как и ожидалось.
Запуск приложения в RenderDoc и захват кадра дает следующее. Qt Quick действительно строит объекты состояния конвейера Vulkan и буферы команд, а код шейдера предоставляется в виде байт-кода SPIR-V.
Во второй части этой серии будет рассмотрено, что Qt 5.14 может предложить для macOS и Windows. После этого перейдем к рассмотрению того, как все это работает под капотом и каковы будут последствия для приложений, требующих нестандартных материалов и эффектов.