Qtブログ(日本語)

Qt 5.10 の Vulkan 対応 - パート1

作成者: 鈴木 佑|Sep 13, 2017 3:17:48 AM

この記事は The Qt BlogVulkan Support in Qt 5.10 – Part 1 を翻訳したものです。
執筆: Laszlo Agocs, 2017年6月6日

すでに ご存知の方もいるか と思いますが、Qt 5.10 の新機能の一つとして Vulkan の基本部分の対応が行われています。Qt 5.9 のリリースも無事完了したので、Vulkan 対応の詳細(未対応の部分も含めて)を実際に見てみましょう。この新機能の紹介を楽しく、簡単にするために、短めの記事をいくつか書くことにしました。また紹介するすべての機能はすでに qtbasedev ブランチにマージ済みですが、Qt 5.10 のリリースまでの間に変更される可能性があるということをここでお伝えしておきます。

動機

OpenGL 以外の描画系の API の対応の開始は Qt 5.8 まで遡ります。この時期には様々な改善のための検討や実際の実装が行われました。主なフォーカスは Qt Quick とその描画エンジンである scenegraph で、プラットフォーム固有の機能を一切使用しないバックエンド(software)と特定のプラットフォーム/ウィンドウイングシステム(Direct3D 12)向けのバックエンドが追加されました。

D3D12 向けの実装の経験 上、こういった API への対応をはじめるのは簡単です。

  1. ネイティブウィンドウハンドラを取得(Windows では QWindow::winId() は HWND になります)
  2. プラットフォーム固有のコードで描画を実行
  3. おしまい!

というわけで、GitHub やその他の場所にあるいくつものプロジェクトですでに証明されているように、おなじような対応で Vulkan にも対応できるというわけです。

実際は、複数のプラットフォームが関わってくるので、もう少しおもしろい感じになります。Vulkan でウィンドウイングシステムを扱うためにはプラットフォーム固有のコードが必要で、クロスプラットフォーム対応のアプリケーションでは ifdefs やそれに似た形での対応をよく見かけます。

私たちは Qt というクロスプラットフォームのフレームワークを開発しているわけで、いつも通り、抽象化のレイヤーを導入することで固有のコードの隠蔽化を行うことになります。

つまり、以下のようなコードではなく、

QWindow *window;

#if defined(VK_USE_PLATFORM_WIN32_KHR)
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.hwnd = (HWND) window->winId();
...
err = vkCreateWin32SurfaceKHR(...);
#elif defined(VK_USE_PLATFORM_WAYLAND_KHR)
VkWaylandSurfaceCreateInfoKHR createInfo;
...
err = vkCreateWaylandSurfaceKHR(...);
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
VkAndroidSurfaceCreateInfoKHR createInfo;
...
err = vkCreateAndroidSurfaceKHR(...)
#elif defined(VK_USE_PLATFORM_XCB_KHR)
VkXcbSurfaceCreateInfoKHR createInfo;
...
err = vkCreateXcbSurfaceKHR(...)
#elif ...

こんな形にしようというわけです。

QWindow *window;

VkSurfaceKHR surface = QVulkanInstance::surfaceForWindow(window);

これにより、ウィンドウシステム固有の処理は Qt のプラットフォーム対応プラグインの中で行われるため、ifdef は必要なくなります。

2つ目の重要な動機は、D3D12 の経験で分かったように、ほとんどのアプリケーションは高レベルで便利なウィンドウ用のクラスで幸せになれるということです。前回は QOpenGLWindow と同じような QD3D12Window クラスを導入しました。クラス継承に関するいくつかの制限が発生はしますが、QWindow ですべてをカバーするようにゼロから設計を見直す(そして上記のコードのように surface をいじくりまわす)必要はありません。

QWindow を直接利用すると、アプリケーションはすべての制御が可能なため、これは当然一番強力な方法ではありますが、後述のとおり、すべての機能が利用可能で安定した Vulkan 対応の QWindow というのは簡単ではありません(スワップチェーンや exposeEvent()、リサイズ、QPlatformSurfaceEvent の対応などなど)。というわけで、QVulkanWindow というクラスを導入することになります。

認識合わせ

新しい QVulkan* クラスの詳細に移る前に、Qt 5.10 での Vulkan 対応をもう少し具体的にしましょう。

  • Qt 5.10 では QWindows でクロスプラットフォームでの Vulkan での描画が可能になり、QVulkanWindow という便利なクラスも追加されました。
  • ウィンドウシステム固有のコードを隠蔽化するために、抽象化のための薄いラッパーを導入し、Vulkan のインスタンスと Vulkan 1.0 API のデバイス固有の部分の対応をしています。
  • Vulkan API 自体の抽象化や隠蔽化はしていません。Qt は Qt としてやるべき事(ウィンドウ関連の処理、プラットフォーム固有の処理、Core API の関数解決)はしますが、それ以外の事はしていません。
  • QWidget::createWindowContainer() を利用することで、Vulkan 対応の QWindow と QWidget の UI とを組み合わせて動かすことが可能です。これは OpenGL 用のウィンドウを利用する場合とまったく同じやり方になります。デスクトップ向けのアプリケーションで、QWidget で 3D を利用している方にはいいお知らせがあります。Vulkan 向けの QGLWidget/QWindow/QOpenGLWindow 相当のクラスを用意しました。
  • 今回の対応において、Qt Quick や Qt 3D、Qt Canvas 3D、QPainter の OpenGL バックエンド、QOpenGLWidget/QQuickWidget などは対象範囲外です。
  • 将来的にその辺の対応も考えてはいますが、5.10 のスコープには含まれていません。

プラットフォーム

では、対応しているプラットフォームを見てみましょう。

Qt 5.10 では、以下の状況になります。

  • Windows(デスクトップ、WinRT は未対応):LunarG SDK がインストールされていて、(それにより)VULKAN_SDK 環境変数が設定されている場合、Qt のビルド時に Vulkan 対応が自動的に有効になります。
  • Linux(現時点では xcb のみ、wayland 対応は後ほど):configure 時に Vulkan のヘッダファイルが見つかった場合に有効になります。
  • Android(API レベル 23 と 24 で確認、Vulkan のヘッダファイル(と関連ツール)はレベル24以降の NDK ではそのまま動きます)

Qt の Vulkan 対応は Vulkan(ローダー)ライブラリに直接リンクはせずに、実行時に動的にシンボルの解決をします。このため、Vulkan の比較的新しめの(1.0.13 以降)ヘッダファイルのみが必要条件になります。

ビルド済みのパッケージの Vulkan 対応に関しては、少なくともいくつかのプラットフォームでは依然として解決しなければいけない問題を抱えています。が、Qt 5.10 までには解決していると思います。

以上でパート1はおしまいです。パート2 では実際の QVulkan クラスたちを紹介しますのでお楽しみに!