Qt RHI:QtQuickのVulkan/Metal/Direct3D対応

この投稿は英語ブログ記事「Qt Quick on Vulkan, Metal, and Direct3D」の和訳です。

Qt 5.14の最初のベータ版公開が近づいているので、主な新機能の一つについてお話したいと思います。この投稿内で、グラフィックスタックの改善点とQt 6に向けての詳細を全てご説明することは難しいので、パート1とパート2にて背景と5.14に関連するものについてお話し、技術的な部分の詳細と今後の方向性については、後に別の投稿でお話しようと思います。

5.14の新機能に関するページ概要グラフィックスAPIに依存しないシーングラフレンダラーの、最初のプレビューがオプトイン機能として追加されました。これにより、OpenGLの代わりにVulkan、Metal、またはDirect3D 11上でほとんどのQt Quickアプリケーションを実行できます。

Qt 6テクニカルビジョンの投稿でご説明したように、Qt 6の主な目標の1つは、Qt上での直接的なOpenGLの使用をできるだけ避け、VulkanMetalDirect3Dなどの幅広いグラフィックスAPIの利用を可能にすることです。 OpenGL(およびOpenGL ES)も選択肢として当然残ります。その背景にある主な動機は、パフォーマンスを発揮する事だけではなく、OpenGLが利用できないか、もしくは既に望ましくないプラットフォームやデバイス上であっても、Qt Everywhereが今後も確かなもので有り続けることを保証することにもあります。それと同時に、最新かつ低レベルなAPIをベースに構築できるため、パフォーマンスの改善(APIオーバーヘッドの減少によるCPU使用率の低下など)や、Qt Quickおよび最近発表されたQt Quick 3Dのようなモジュールの背後にあるレンダリングエンジンの可能性も広がります。

さらに、プラットフォームで最もサポートされているグラフィックスAPIでレンダリングできることは、Qtを使用してUIをレンダリングしながら独自のネイティブ2Dまたは3Dグラフィックレンダリングを実行するアプリケーションにとって嬉しいポイントです。

そのような場合、使用するグラフィックスAPIの決定に関しては、Qtに主導権が無いことも多々あります。たとえば、macOSのデスクトップアプリケーションが、2DのレンダリングをQt Quickに依存しながら独自の3DコンテンツにMetalを使用したい場合、Qt QuickもMetal経由でレンダリングすると非常に便利です。これは、Qt 5.xのグラフィックスの進化過程を見ていた人には馴染みがあるのでは無いでしょうか。概念的には、OpenGLコアプロファイルコンテキストでの操作のサポートがQt Quickレンダラーに導入されたときと変わりません。Qt Quick自体にはその必要性や関連はありませんが、コアプロファイル機能に関連付けられた外部レンダリングコードを統合できるようにするために、Qt Quick側でそれを認識し、対応できる必要がありました。つまり、これはQt 5で行われてきたことからの自然な流れであり、現在OpenGL以外のグラフィックスAPIをカバーするように拡張されています。

これにより、次の2つの疑問点が上がってくることと思います。

  • Qt 5.xにどのように関連しているのか?全てQt 6の要素ではないのか?
  • なぜ<なんらかのグラフィックスAPI変換ソリューション>を使用して標準化しないのか?

Qt 5.14には何が含まれているのか

Qtのグラフィックスの完全なオーバーホールを行うことは、Qt 6のターゲットになっています。しかし、Qt 5.x上での作業や開発を止めて、一度に全てが上手くいくことを期待するのは現実的ではありません。Qt開発者がよく口にするように、APIの最初の試作は”最適”というには満たない傾向にあります。そのため、並行して開発を行う代わりに、QtのUIテクノロジーであるQt Quickに焦点をあてていきます。

Qt 5.14には、新しいQt Quickレンダリングパスのプレビュー版が付属される予定です。 デフォルトでは、非アクティブであるため、アプリケーションに対して目に見える変更は一切ありません。内部では、以前のバージョンと同じダイレクトOpenGLベースのコードパスを通過します。 ただし、新しいアプローチを試してみたい人は、環境変数を設定するか、main関数のC ++ APIを介して要求することでオプトインできます。 ここでは、フィードバックを早期に取得して、Qt 6.0のリリースを待たずに繰り返し、発展させていけることを期待しています。

Qt 5.14ドキュメントをみると、次のようなことがわかります。

Qt 5.14 doc screenshot for enabling RHI-based rendering in Qt Quick.

QSG_RHIをセットして実行すると、すべてのアプリケーションがすぐに動作するわけではありません。ダイレクトOpenGL呼び出しを実行するシーングラフノード、またはカスタムマテリアルにGLSLシェーダーコードを含むカスタムQQuickItemは、RHIベースのレンダリングを有効にすると機能しなくなります。同じことが、GLSLソースコードを含むShaderEffectアイテムにも当てはまります。 カスタムマテリアルとエフェクトを最新の方法で実行するためのソリューションは、ほとんどがすでに存在しますが、必要に応じてアプリケーションを移行する必要があります。アーリーアダプターは、5.14および5.15のタイムフレームで試すことも出来ますが、Qt 6.0より前での幅広い普及と移行は期待されていません。 一方で、多くの既存QMLアプリケーションは、ベースとなるレンダリングエンジンがVulkanやMetalなどのまったく異なるAPIを経由する場合でも機能する可能性があります。

何故トランスレーションレイヤーXXXではないのか?

なにより先に、もし仮に常に直ぐに使える状態でなかったとしても、MoltenVKMoltenGLANGLEZinkなどのAPIやシェーダートランスレーションレイヤーを使用する際の実現性について予めお伝えしておく必要があります。たとえば、MoltenVKでは、macOSのVulkanを介してQt Quick UIをレンダリングすることもできます。 Qt QuickアプリケーションがVulkanのみを使用したい、かつmacOSに対応したい場合、MoltenVKはひとつの選択肢です。(適切に構成されたQtビルドが使用され、MoltenVKがユーザーのシステムで利用可能な限り)

このようなトランスレーションレイヤーの存在を前提とし、それゆえにQtに含め、一緒にデプロイすることは全く別の話です。

  • Qt Everywhere(Qtはどこでも動作する)をQt Only Where External Dependencies Allow(Qtは外部依存に応じて動作する)に変更するのは、言うまでもなく理想的ではありません。
    Qtは、通常考えられるよりもはるかに多くのプラットフォームと環境を対象としています。 特殊な環境でもコンパイルして動作し、特別なニーズが生じた場合にも簡単に適応でき、絶対に必要な外部依存にのみ、Qtは依存することができます。(例えば、INTEGIRY、QNX、カスタマイズされた組込Linux環境、不完全もしくは開発中のグラフィックススタックを備えたシステム、独自のAPIやシステムを考えてみてください。そして場合によってはベンダー特有であったり標準とは異なる方法で様々なグラフィックスAPIに対応しなければなりません。これには、Qtのレンダリングスタックのすべてのレベルで柔軟にカスタマイズ出来る必要があります)
  • 実行時にシェーディング言語(または中間形式)間で翻訳を行うことも、理想的ではありません。 Qt 6のシェーダーパイプラインは、オフラインで、または少なくともアプリケーションのビルド時により多くの処理を行うことが期待されています。 トランスレーションレイヤーを使用して、どのAPI・シェーディング言語が使用されているかを隠してしまうと、実際のAPIのシェーダーまたはバイトコードを準備、または利用できないため、そうした努力はすぐに無駄になってしまいます。
  • 話題に上がった選択肢のいくつかは、今のところ実用的ではありません。OpenGL(ES)は主力であり、今後も当面の間はそうです。 したがって、単一のAPIを直接使用し続けるということは、そのAPIがOpenGLであること、またはOpenGLをターゲットにできるトランスレーションレイヤーであることを意味します(低パフォーマンスのデバイスでも十分効率的です)。
  • Qtのレンダリングエンジンがアプリケーション独自のネイティブなレンダリングコードによって補完される場合は、両者が同じAPIを直接使用する場合に最適な動作をする傾向にあります。この点で、トランスレーションレイヤーの使用は、基礎となるネイティブオブジェクトへのアクセスを許可する場合、必ずしも障害ではありません。(たとえば、WindowsのANGLEの上でQt 5を実行するときに、EGL拡張によってDirect3D-Qt Quick間の相互動作が実現されています)しかし、これは常に可能であるとは限らず、また可能であったとしても、別の問題が出てくる可能性があります。
  • また、ライセンスへの影響と問題もあります。たとえばApache 2.0にはGPLv2と互換性がありません。どんな場合にも、商用のみのソリューションに依存することはできません。
  • これまでの経験からすると(ANGLEの一部とMoltenVKの一部)、こういったソリューションの適用は最初に期待するほど簡単にはいきません。 ある時点で、すべてのオプションを稼働状態に保つためのコストがかかりすぎるかもしれません。そういった努力をするよりも、正しい方向でネイティブAPIに注力したほうが良いでしょう。 これらのトランスレーションソリューションの、本質的にプラットフォームに依存するという性質も理想的ではありません。仮に他のソリューションをQtがターゲットとするすべてのプラットフォームに適用すると、すぐに状況は支えきれないものになります。

そのため、Qtは低レベルAPIトランスレーターに依存する代わりに、3Dグラフィックスの独自の高レベルな抽象化を定義します(当面は内部使用向け)。これは、その裏側でAPI固有のバックエンド実装に変換される、Qtの多くのコンポーネントでよく使われているパターンです。場合によっては、バックエンドは特定のプラットフォームを対象としていますが、(Metal、Direct3D)、1つのバックエンド・1つのAPIで複数のプラットフォームを対象とするものもあります(Vulkan、OpenGL)。これは、glslangSPIRV-Crossなどのいくつかのサードパーティプロジェクトに基づいた新しいシェーダー管理パイプラインによって補完されます。より細かい部分については、この投稿内で後ほど説明します。ひとまず、スタックについて触れて、Qt 5.14のQt Quickにてこれが何を可能にするかを見てみましょう。

本当に機能するのか?

QUIt Codingの有名なQt5 Cinematic Experienceデモアプリケーションを例として見てみましょう。 Qt Quickシーングラフの両方のレンダリングパスで機能するように、いくつかのShaderEffectアイテムをわずかに修正しています。そのコードはこちらにあります。

:QSG_INFO = 1を設定してアプリケーションを通常どおり起動すると、次のようになります。

Screenshot from 2019-09-13 13-19-02

デバッグ出力ログが示すように、これはLinuxデスクトップのOpenGLで実行されています。

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に設定すると、どのようになるでしょうか。

Screenshot from 2019-09-13 13-36-30

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を直接使用することはなく、Qt Quickシーングラフ内を飛び回るGLSLシェーダーソースはもうありません。 代わりに、レンダリングはQt Rendering Hardware Interface、QRhiで行います。(当面はQtGuiモジュールのプライベートAPI)

では早速、QSG_RHI_BACKEND = vulkanを設定してみましょう。Screenshot from 2019-09-13 13-41-14

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バイトコードとして提供されています。

Screenshot from 2019-09-13 14-02-39

今のところ、このような感じです。 このシリーズの第2部では、macOSおよびWindows用にQt 5.14が提供するものについて見ていきます。 その後、これらすべてが内部でどのように機能するか、およびカスタムマテリアルとエフェクトを必要とするアプリケーションにどのような影響があるかを考察します。

お楽しみに!


Blog Topics:

Comments