Qt Quick シーングラフ

のシーングラフQt Quick

Qt Quick 2のシーングラフは、OpenGL ES、OpenGL、Vulkan、Metal、またはDirect 3DなどのグラフィックスAPIを介してトラバースおよびレンダリングされる専用のシーングラフを使用します。従来の命令型ペイントシステム( など)ではなく、グラフィックスにシーングラフを使用することは、レンダリングするシーンをフレーム間で保持でき、レンダリングを開始する前にレンダリングするプリミティブの完全なセットを知ることができることを意味します。これにより、状態変化を最小化するバッチレンダリングや、不明瞭なプリミティブの破棄など、さまざまな最適化が可能になります。QPainter

たとえば、ユーザーインターフェースに10項目のリストがあり、各項目に背景色、アイコン、テキストがあるとする。従来の描画テクニックを使用すると、30回の描画呼び出しと同程度の状態変更が発生します。一方、シーングラフでは、すべての背景が1回の呼び出しで描画され、次にすべてのアイコンが描画され、次にすべてのテキストが描画されるように、レンダリングするプリミティブを再編成することができます。

シーングラフは、Qt Quick 2.0と密接に結びついており、スタンドアロンでは使用できません。シーン・グラフはQQuickWindow クラスによって管理され、レンダリングされます。カスタムItemタイプは、QQuickItem::updatePaintNode()を呼び出すことによって、そのグラフィカル・プリミティブをシーン・グラフに追加することができます。

シーングラフはItemシーンのグラフィカルな表現であり、すべてのItemをレンダリングするのに十分な情報を含む独立した構造である。一度セットアップされると、アイテムの状態とは無関係に操作し、レンダリングすることができる。多くのプラットフォームでは、GUIスレッドが次のフレームのステートを準備している間、シーングラフは専用のレンダースレッドでレンダリングされます。

注意: このページに記載されている情報の多くは、Qt Quick シーングラフのビルトイン、デフォルトの動作に特有のものです。software 適応のような、別のシーングラフ適応を使用する場合、すべての概念が適用されるとは限りません。異なるシーングラフ適応の詳細については、シーングラフ適応を参照してください。

Qt Quick シーングラフの構造

シーングラフは多くの定義済みのノードタイプで構成され、それぞれが専用の目的を果たす。私たちはこれをシーングラフと呼んでいますが、より正確な定義はノードツリーです。ツリーはQMLシーン内のQQuickItem タイプから構築され、内部的にはシーンを描画するレンダラーによって処理されます。ノード自体には、アクティブな描画コードや仮想paint() 関数は含まれていません

ノードツリーの大部分は、既存のQt Quick QMLタイプによって内部的に構築されますが、3Dモデルを表すサブツリーを含め、ユーザーが独自のコンテンツを持つ完全なサブツリーを追加することも可能です。

ノード

ユーザーにとって最も重要なノードは、QSGGeometryNode です。これは、ジオメトリとマテリアルを定義してカスタムグラフィックを定義するために使用されます。ジオメトリはQSGGeometry を使用して定義され、グラフィカルプリミティブの形状またはメッシュを記述します。線、矩形、多角形、多数の切断された矩形、または複雑な3Dメッシュにすることができます。マテリアルは、この形状のピクセルがどのように塗りつぶされるかを定義します。

ノードは任意の数の子を持つことができ、ジオメトリノードは子順に表示され、子の後ろに親があるようにレンダリングされます。

注意: これはレンダラーでの実際のレンダリング順序については何も言いません。視覚的な出力のみが保証されます。

使用可能なノードは次のとおりです:

QSGClipNode

シーングラフのクリッピング機能の実装

QSGGeometryNode

シーングラフ内のすべてのレンダリングコンテンツに使用されます。

QSGNode

シーングラフ内のすべてのノードのベースクラス

QSGOpacityNode

ノードの不透明度を変更するために使用されます。

QSGTransformNode

シーングラフ内のトランスフォームの実装

カスタムノードは、QQuickItem::updatePaintNode ()をサブクラス化し、QQuickItem::ItemHasContents フラグを設定することで、シーングラフに追加されます。

警告 ネイティブ・グラフィックス(OpenGL、Vulkan、Metalなど)の操作とシーングラフとのインタラクションは、主にupdatePaintNode()呼び出しの間、レンダースレッドでのみ行われることが重要です。経験則では、"QSG" 接頭辞を持つクラスのみをQQuickItem::updatePaintNode() 関数内で使用します。

詳細は、シーングラフ - カスタムジオメトリを参照してください。

前処理

ノードには仮想のQSGNode::preprocess() 関数があり、シーングラフがレンダリングされる前に呼び出されます。ノードのサブクラスは、フラグQSGNode::UsePreprocess を設定し、QSGNode::preprocess() 関数をオーバーライドして、ノードの最終準備を行うことができます。例えば、ベジェ曲線を現在のスケールファクターに適した詳細レベルに分割したり、テクスチャの一部を更新したりします。

ノードの所有権

ノードの所有権は、作成者が明示的に行うか、シーングラフがフラグQSGNode::OwnedByParent を設定することで行います。シーングラフの所有権をシーングラフに割り当てることは、シーングラフがGUIスレッドの外にあるときにクリーンアップを簡単にするため、しばしば好ましいことです。

マテリアル

マテリアルは、QSGGeometryNode のジオメトリの内部がどのように塗りつぶされるかを記述します。グラフィックスパイプラインの頂点とフラグメントステージ用のグラフィックスシェーダをカプセル化し、実現できることに十分な柔軟性を提供しますが、Qt Quick アイテム自体のほとんどは、ソリッドカラーやテクスチャフィルなどの非常に基本的なマテリアルしか使用しません。

QMLアイテムタイプにカスタムシェーディングを適用したい場合、ShaderEffect を使ってQMLで直接シェーディングを行うことができます。

以下はマテリアルクラスの完全なリストです:

QSGFlatColorMaterial

シーングラフでソリッドカラーのジオメトリをレンダリングする便利な方法です。

QSGMaterial

シェーダプログラムのレンダリング状態のカプセル化

QSGMaterialShader

グラフィックスAPIに依存しないシェーダープログラムを表します。

QSGMaterialType

QSGMaterial と組み合わせて一意の型トークンとして使用されます。

QSGOpaqueTextureMaterial

シーングラフでテクスチャ付きジオメトリをレンダリングする便利な方法

QSGTextureMaterial

シーングラフでテクスチャ付きジオメトリをレンダリングする便利な方法

QSGVertexColorMaterial

シーングラフで頂点ごとに色付けされたジオメトリをレンダリングする便利な方法

便利なノード

シーングラフAPIは低レベルで、利便性よりもパフォーマンスに重点を置いています。最も基本的なものであっても、カスタムジオメトリやカスタムマテリアルをゼロから書くには、自明ではない量のコードが必要です。そのため、APIにはいくつかの便利なクラスがあり、最も一般的なカスタムノードを簡単に利用できるようになっています。

シーングラフとレンダリング

シーングラフのレンダリングはQQuickWindow クラスの内部で行われ、アクセスするためのパブリック API はありません。しかし、レンダリングパイプラインには、ユーザーがアプリケーションコードを追加できる場所がいくつかあります。これは、カスタムシーングラフコンテンツを追加したり、シーングラフで使用されているグラフィックスAPI(OpenGL、Vulkan、Metalなど)を直接呼び出して任意のレンダリングコマンドを挿入するために使用できます。統合ポイントは、レンダリングループによって定義されます。

シーングラフ・レンダラーがどのように動作するかについての詳しい説明は、Qt Quick Scene Graph Default Rendererを参照してください。

利用可能なレンダリングループは2種類あります:basic threaded basic はシングルスレッドで、 は専用スレッドでシーングラフレンダリングを行います。Qt は、プラットフォームと、場合によっては使用中のグラフィックドライバに基づいて、適切なループを選択しようとします。これが満足のいくものでない場合、またはテスト目的の場合、環境変数 を使用して、指定されたループを強制的に使用することができます。どのレンダリングループが使用されているかを確認するには、 を有効にしてください。threaded QSG_RENDER_LOOP qt.scenegraph.general logging category

スレッドレンダーループ ('threaded')

多くの設定では、シーングラフのレンダリングは専用のレンダースレッドで行われます。これは、マルチコアプロセッサの並列性を高め、ブロッキングスワップバッファ呼び出し待ちなどのストール時間を有効活用するために行われます。これにより、パフォーマンスが大幅に向上しますが、シーングラフとのインタラクションが発生する場所とタイミングに一定の制限が課されます。

以下は、スレッドレンダーループとOpenGLでフレームがどのようにレンダリングされるかの簡単な概要です。この手順は、OpenGLコンテキストの仕様を除けば、他のグラフィックスAPIでも同じです。

  1. QMLシーンに変更が発生し、QQuickItem::update() 。これは例えばアニメーションやユーザー入力の結果です。レンダースレッドにイベントがポストされ、新しいフレームが開始されます。
  2. レンダースレッドは新しいフレームを描画する準備をし、GUIスレッド上でブロックを開始します。
  3. レンダー・スレッドが新しいフレームを準備している間に、GUIスレッドはQQuickItem::updatePolish ()を呼び出し、レンダリングする前にアイテムの最終的なタッチアップを行う。
  4. GUIスレッドはブロックされる。
  5. QQuickWindow::beforeSynchronizing() シグナルが発せられる。アプリケーションはこのシグナルに(Qt::DirectConnection を使って)直接接続し、QQuickItem::updatePaintNode ()を呼び出す前に必要な準備を行うことができます。
  6. QMLの状態をシーングラフに同期させます。これは、QQuickItem::updatePaintNode ()関数を、前のフレームから変更された全てのアイテムに対して呼び出すことで行われます。QML アイテムとシーングラフのノードが相互作用するのはこの時だけです。
  7. GUIスレッドブロックが解放されます。
  8. シーングラフがレンダリングされる:
    1. QQuickWindow::beforeRendering() シグナルが発行されます。アプリケーションはこのシグナルに(Qt::DirectConnection を使って)直接接続し、QMLシーンの下に視覚的にスタックするカスタムグラフィックスAPIコールを使用することができます。
    2. QSGNode::UsePreprocess を指定したアイテムは、QSGNode::preprocess() 関数が呼び出されます。
    3. レンダラーがノードを処理します。
    4. レンダラーは状態を生成し、使用中のグラフィックス API の描画コールを記録します。
    5. QQuickWindow::afterRendering() シグナルが発行されます。アプリケーションはこのシグナルに(Qt::DirectConnection を使用して)直接接続し、QML シーン上に視覚的にスタックするカスタム グラフィックス API 呼び出しを発行できます。
    6. フレームの準備ができました。バッファがスワップされるか(OpenGL)、現在のコマンドが記録され、コマンドバッファがグラフィックキューに投入されます(Vulkan、Metal)。QQuickWindow::frameSwapped() が出力されます。
  9. レンダリングスレッドがレンダリングしている間、GUIは自由にアニメーションを進めたり、イベントを処理したりできます。

スレッドレンダラは現在、Direct3D 11とopengl32.dll使用時のOpenGLを使用するWindows、Mesa llvmpipeを除くLinux、Metalを使用するmacOS、モバイルプラットフォーム、EGLFSを使用するEmbedded Linux、およびプラットフォームに関係なくVulkanでデフォルトで使用されます。これらはすべて将来のリリースで変更される可能性があります。環境にQSG_RENDER_LOOP=threaded を設定することで、スレッドレンダラーを強制的に使用することは常に可能です。

非スレッドレンダーループ ('basic')

システム標準のopengl32.dllを使用しない場合、OpenGLを使用するWindows、OpenGL、WebAssemblyを使用するmacOS、および一部のドライバを使用するLinuxでは、現在、非スレッドレンダーループがデフォルトで使用されています。後者については、OpenGLドライバとウィンドウ・システムのすべての組み合わせがテストされていないため、これはほとんど予防措置です。

macOSとOpenGLでは、XCode 10 (10.14 SDK)以降でビルドする場合、スレッドレンダーループはサポートされません。Xcode 9(10.13 SDK)でビルドしてレイヤーバッキングをオプトアウトすることができます。Metalではそのような制限はありません。

Webプラットフォームでは、メインスレッド以外のスレッドでWebGLを使用するためのサポートが制限されており、メインスレッドをブロックするためのサポートも制限されているため、スレッド化されたレンダリングループはWebAssemblyではサポートされていません。

スレッド化されていないレンダリングループを使用する場合でも、スレッド化されたレンダラーを使用しているかのようにコードを記述する必要があります。

以下は、非スレッドレンダラーでのフレームレンダリングシーケンスの簡略図です。

アニメーションの駆動

上記の図において、Advance Animations は何を指しているのでしょうか?

デフォルトでは、Qt Quick のアニメーション(NumberAnimation など)は、デフォルトのアニメーション・ドライバによって駆動されます。これはQObject::startTimer() のような基本的なシステムタイマーに依存しています。タイマーは通常16ミリ秒の間隔で動作します。これは完全に正確であることはなく、基盤となるプラットフォームのタイマーの精度にも依存しますが、レンダリングから独立しているという利点があります。ディスプレイのリフレッシュレートや、ディスプレイの垂直同期への同期が有効かどうかに関係なく、均一な結果が得られます。これが、basic レンダー・ループを使ったアニメーションの動作です。

レンダリングループの設計(シングルスレッドであれ、マルチスレッドであれ)に依存せず、画面上のスタッターを抑えて、より正確な結果を提供するために、レンダリングループは、独自のカスタムアニメーションドライバをインストールし、タイマーに頼らずに、advancing

これが、threaded レンダー・ループが実装しているものだ。1つはguiスレッド上(NumberAnimation のような通常のアニメーションを駆動するため)で、もう1つはレンダースレッド上(レンダースレッドのアニメーション、つまりOpacityAnimatorXAnimator のようなAnimator タイプを駆動するため)です。これらの両方は、フレームの準備中に進められます。つまり、アニメーションはレンダリングと同期します。これは、基礎となるグラフィックスタックによって、プレゼンテーションがディスプレイの垂直同期にスロットルされるためです。

したがって、上記のthreaded レンダリングループの図では、両方のスレッドに明示的なAdvance animations ステップがあります。レンダースレッドにとって、これは些細なことです。スレッドがvsyncにスロットルされているので、16.67ミリ秒が経過したかのように各フレームでアニメーション(Animator タイプの場合)を進めると、システムタイマーに頼るよりも正確な結果が得られます。(vsyncのタイミングにスロットルされた場合、60Hzのリフレッシュレートで1000/60 ミリ秒であるため、前のフレームで同じ操作が行われてからおよそその時間が経過していると考えるのが妥当です)

guiスレッドとレンダースレッド間のデータの同期が不可欠なため、guiスレッドはレンダースレッドと同じレートに効果的にスロットルされます。

上記の例では60フレーム/秒を使用しましたが、Qt Quick は他のリフレッシュレートにも対応しています。レートはQScreen とプラットフォームから照会されます。たとえば、144Hzの画面では、間隔は6.94ミリ秒です。というのも、レンダリングループが考えていることが現実に合っていないと、アニメーションのペーシングが正しくなくなるからです。

注意: Qt 6.5 から、スレッド化されたレンダリングループは、経過時間 (QElapsedTimer) のみに基づいて、別のアニメーションドライバを選択できるようになりました。これを有効にするには、QSG_USE_SIMPLE_ANIMATION_DRIVER 環境変数をゼロ以外の値に設定します。これには、複数のウィンドウがある場合にQTimer にフォールバックするためのインフラストラクチャが不要であること、vsync ベースのスロットリングが欠落しているか壊れているかを判断するためのヒューリスティックが不要であること、vsync スロットリングのあらゆる種類の時間的ドリフトと互換性があること、プライマリ画面のリフレッシュレートに縛られないこと、したがってマルチ画面セットアップでよりよく動作する可能性があること、などの利点があります。また、vsyncベースのスロットリングが壊れているか無効になっていても、レンダースレッドアニメーション(Animator タイプ)を正しく駆動します。一方、このアプローチでは、アニメーションが滑らかでないと感じられるかもしれません。互換性を考慮し、現時点ではオプトイン機能として提供されています。

まとめると、threaded のレンダリングループは、以下の条件が満たされている限り、スタッターの少ないよりスムーズなアニメーションを提供することが期待されます:

  • 画面上に(QQuickWindow のように)ウィンドウが1つだけある。
  • VSyncベースのスロットリングは、underylingグラフィックスとディスプレイスタックで期待通りに動作します。

表示されているウィンドウが1つもない、または複数ある場合はどうなりますか?

レンダリング可能なウィンドウがない場合、例えば、QQuickWindow が最小化されていたり(Windows)、完全に隠されていたり(macOS)する場合、フレームを表示することができないため、画面のリフレッシュレートと同期して「動作」するスレッドに依存することができません。この場合、threaded のレンダー・ループは、自動的にシステム・タイマー・ベースのアプローチに切り替わってアニメーションを駆動します。つまり、basic のループが使用するメカニズムに一時的に切り替わります。

画面上に複数のQQuickWindow インスタンスがある場合も同様です。レンダースレッドとの同期によって可能になる、guiスレッド上でアニメーションを進めるための上記モデルは、複数のレンダースレッドと複数の同期ポイントが存在するようになったため、満足のいくものではなくなりました。(なぜなら、guiスレッドがブロックする時間と頻度は、ウィンドウ内のコンテンツ(それらはアニメーションしているのか、それらはどのくらいの頻度で更新されているのか)やグラフィックスタックの動作(wait-for-vsyncで提示される2つ以上のスレッドを正確にどのように処理するのか)を含む、多くの要因に依存するようになったからです。)私たちは、安定したクロスプラットフォームな方法で、ウィンドウ(そもそも、どのウィンドウなのか)の表示速度にスロットルされることを保証できないため、アニメーションをレンダリングに基づいて進めることはできません。

このアニメーション処理メカニズムの切り替えは、アプリケーションには透過的です。

もしvsyncベースのスロットリングが機能不全に陥っていたり、グローバルに無効になっていたり、アプリケーション自身が無効にしていたらどうでしょう?

threaded 例えば、OpenGL(GLX、EGL、WGL)の場合、スワップ間隔1を要求する、Direct 3Dの場合、間隔1でPresent()を呼び出す、またはVulkanでプレゼンテーションモードFIFO

グラフィックスドライバによっては、Qt の要求を無視して、この設定を上書きしてオフにすることができます。この例として、vsync に関するアプリケーションの設定を上書きできる、グラフィックスドライバのシステム全体のコントロールパネルがあります。また、グラフィックススタックが適切な vsync ベースのスロットリングを提供できないこともあり、これは仮想マシンによっては起こりえます(主に OpenGL や Vulkan のソフトウェアラスタライゼーションベースの実装を使用しているため)。

swap/present操作(または他のグラフィック操作)をブロックしないと、そのようなレンダリングループはアニメーションを速く進めすぎてしまいます。basic のレンダリングループでは、常にシステムタイマーに依存しているため、このような問題は発生しません。threaded では、Qt のバージョンによって動作が異なります:

  • システムが vsync ベースのスロットリングを提供できないことが分かっている場合、Qt 6.4 以前の唯一の選択肢は、アプリケーションを実行する前にQSG_RENDER_LOOP=basic を環境に手動で設定して、basic レンダーループを使用することでした。
  • Qt 6.4 からは、QSG_NO_VSYNC 環境変数を 0 以外の値に設定するか、ウィンドウのQSurfaceFormat::swapInterval() を0 に設定することで、問題を軽減することができます。threaded レンダリングループは、vsync に依存してアニメーションを駆動することは無駄であることを認識し、複数のウィンドウで使用するのと同じように、システムタイマーを使用するように戻ります。
  • さらに良いことに、Qt 6.4 から scenegraph は、いくつかの簡単なヒューリスティックを使って、フレームが「速すぎる」ことを認識し、必要であれば自動的にシステムタイマーに切り替えます。これは、ほとんどの場合、何もする必要がなく、デフォルトのレンダー・ループがthreaded 、アプリケーションが期待通りにアニメーションを実行することを意味します。これはアプリケーションにとっては透過的ですが、トラブルシューティングや開発目的では、QSG_INFO またはqt.scenegraph.general が有効になっているときに出力される"Window 0x7ffc8489c3d0 is determined to have broken vsync throttling ..." メッセージでログに記録されることを知っておくと便利です。この方法は、最初に評価するためのデータを収集する必要があるため、QQuickWindow を開いたときに、アプリケーションが短時間だけ過度に速いアニメーションを表示する可能性があることを意味します。さらに、vsyncが壊れた可能性のあるすべての状況を捕捉できない可能性があります。

しかし、設計上、スレッド・アニメーション(Animator タイプ)のレンダリングに役立つものはないことを忘れないでください。vsync ベースのブロッキングがない場合、animators は、通常のanimations で回避策が有効になっていても、デフォルトでは不正確に、予想よりも速く進みます。これが問題になる場合は、QSG_USE_SIMPLE_ANIMATION_DRIVER を設定して、代替アニメーション・ドライバを使用することを検討してください。

注: GUI(メイン)スレッド上のレンダリングループロジックとイベント処理は、 vsync待機が無効になっていても、必ずしもスロットルされていないわけでは ないことに注意してください: 両方のレンダリングループは、QWindow::requestUpdate() を介してウィンドウの更新をスケジュールします。これは、イベント処理の時間を確保するために、ほとんどのプラットフォームで5ミリ秒のGUIスレッドタイマーに支えられています。macOSなど一部のプラットフォームでは、プラットフォーム固有のAPI(CVDisplayLinkなど)を使用して、新しいフレームを準備する適切な時間について通知を受けており、おそらく何らかの形でディスプレイのvsyncに関連付けられています。これはベンチマークや同様の状況に関連する可能性があります。低レベルのベンチマークを実行しようとするアプリケーションやツールでは、QT_QPA_UPDATE_IDLE_TIME 環境変数を0 に設定すると、GUI スレッドのアイドル時間を短縮できる可能性があります。通常のアプリケーションの使用では、ほとんどの場合、デフォルトで十分です。

注: レンダリングやアニメーションが期待されるペースで実行されない理由を明らかにする手がかりになるかもしれないので、疑わしい場合は、トラブルシューティングのためにqt.scenegraph.generalqt.scenegraph.time.renderloop のログ・カテゴリーを有効にしてください。

QQuickRenderControlによるレンダリングのカスタムコントロール

QQuickRenderControl を使用する場合、レンダリングループを駆動する責任はアプリケーションに移ります。この場合、組み込みのレンダリングループは使用されません。代わりに、適切なタイミングでポリッシュ、同期、レンダリングの各ステップを呼び出すのはアプリケーションに任されます。上に示したものと同様に、スレッドまたは非スレッドの動作を実装することが可能です。

さらに、アプリケーションは、QQuickRenderControl と組み合わせて独自の QAnimationDriver を実装し、インストールすることもできます。これにより、Qt Quick アニメーションの駆動を完全に制御することができます。これは、画面上に表示されないコンテンツにとって特に重要であり、フレームの提示が行われないため、プレゼンテーション・レートとは無関係です。これはオプションで、デフォルトではアニメーションはシステムタイマーに基づいて進みます。

QRhiベースとネイティブ3Dレンダリングによるシーングラフの拡張

シーングラフは、アプリケーションが提供するグラフィックコマンドを統合するための3つの方法を提供します:

  • QRhi 、OpenGL、Vulkan、Metal、Direct3Dコマンドを、シーングラフ自身のレンダリングの前後に直接発行します。これは事実上、メインのレンダーパスに一連の描画呼び出しを前置または追加します。追加のレンダーターゲットは使用されません。
  • テクスチャへのレンダリングと、シーングラフ内のテクスチャノードの作成。これは、追加のレンダーパスとレンダーターゲットを伴います。
  • シーングラフ内のQSGRenderNode サブクラスをインスタンス化することによって、シーングラフ自身のレンダリングとインラインで描画コールを発行する。これは、最初のアプローチに似ていますが、カスタム描画コールは、シーングラフのコマンドストリームに効果的に注入されます。

アンダーレイ・オーバーレイモード

QQuickWindow::beforeRendering() およびQQuickWindow::afterRendering() シグナルに接続することで、アプリケーションは、シーングラフがレンダリングしているのと同じコンテキストに、QRhi またはネイティブ 3D API 呼び出しを直接行うことができます。VulkanやMetalのようなAPIを使用すると、アプリケーションは、シーングラフのコマンドバッファのようなネイティブオブジェクトに、QSGRendererInterface 。シグナルの名前が示すように、ユーザーは、Qt Quick シーンの下に、またはその上にコンテンツをレンダリングすることができます。この方法で統合する利点は、レンダリングを実行するために余分なレンダーターゲットを必要とせず、おそらく高価なテクスチャリングステップが不要になることです。欠点は、カスタムレンダリングはQt Quick 自身のレンダリングの最初か最後にしか発行できないことです。QQuickWindow シグナルの代わりにQSGRenderNode を使用すると、この制限は多少緩和されますが、深度テストに依存して深度書き込みを有効にしてレンダリングすると、カスタム コンテンツとQt Quick コンテンツの深度バッファの使用が競合する状況が簡単に発生するため、いずれの場合も 3D コンテンツと深度バッファの使用には注意が必要です。

Qt 6.6 以降、QRhi API はセミパブリック、つまりアプリケーションに提供され、限定的な互換性保証はあるものの、ドキュメント化されています。これにより、シーングラフ自体が使用するのと同じグラフィックスとシェーダの抽象化を使用することで、移植性の高い、クロスプラットフォームの2D/3Dレンダリングコードを作成することができます。

Scene Graph - RHI Under QMLの例では、QRhi を使ったアンダーレイ・オーバーレイアプローチの実装方法を紹介しています。

Scene Graph - OpenGL Under QMLの例では、OpenGL を使って、これらのシグナルをどのように使うかの例を示しています。

Scene Graph - Direct3D 11 Under QML の例では、Direct3Dを使用したシグナルの使用例を示しています。

Scene Graph - Metal Under QML の例では、これらのシグナルを Metal で使用する例を示しています。

Scene Graph - Vulkan Under QMLの例では、Vulkan を使ったこれらのシグナルの使い方の例を示しています。

Qt 6.0から、基礎となるグラフィックスAPIの直接使用は、QQuickWindow::beginExternalCommands ()とQQuickWindow::endExternalCommands ()の呼び出しで囲む必要があります。このコンセプトは、QPainter::beginNativePainting() から馴染みがあるかもしれませんが、同様の目的を果たします。Qt Quick Scene Graph が、現在記録されているレンダーパス内の状態に関するキャッシュされた状態や仮定があれば、それが現在無効であることを認識できるようにします。これは、アプリケーションコードが、基礎となるグラフィックスAPIを直接使用することによって変更された可能性があるためです。これは、QRhi を使用する場合には適用されず、必要でもありません。

カスタムOpenGLレンダリングとシーングラフを混在させる場合、アプリケーションがOpenGLコンテキストを、バッファがバインドされた状態、属性が有効な状態、zバッファやステンシルバッファに特別な値がある状態などにしないことが重要です。そうすると、予測できない動作になる可能性があります。

カスタムレンダリングコードは、アプリケーションのGUI(メイン)スレッドで実行されることを想定してはならないという意味で、スレッドアウェアでなければなりません。QQuickWindow シグナルに接続する場合、アプリケーションはQt::DirectConnection を使用し、接続されたスロットがシーングラフ専用のレンダースレッドがある場合は、そのスレッドで起動されることを理解する必要があります。

テクスチャベースのアプローチ

テクスチャベースの選択肢は、アプリケーションが、Qt Quick シーン内のカスタム 3D レンダリングの「平坦化された」2D 画像を持つ必要がある場合に、最も柔軟なアプローチです。これはまた、メインのレンダーパスで使用されるバッファとは独立した専用の深度/ステンシルバッファを使用することもできます。

OpenGLを使用する場合、レガシーコンビニエンスクラスQQuickFramebufferObjectQRhiOpenGL以外のカスタムレンダラーやグラフィックスAPIも、このアプローチに従うことができます(ただし、QQuickFramebufferObject は現在サポートしていません)。基礎となる API で直接テクスチャを作成してレンダリングし、続いてカスタムQQuickItemQt Quick シーン内でこのリソースをラッピングして使用する方法を、次の例で示します:

Scene Graph - RHI Texture Item の例。

Scene Graph - Vulkan Texture Import の例。

Scene Graph - Metal Texture Import の例。

インラインアプローチ

QSGRenderNode を使用すると、カスタム描画コールは、シーングラフのレンダーパスの記録の最初でも最後でもなく、シーングラフのレンダリングプロセス中に注入されます。これは、QRhi または OpenGL、Vulkan、Metal、Direct 3D などのネイティブ 3D API 経由でグラフィックスコマンドを発行できるように特別に存在するシーングラフノード、QSGRenderNode のインスタンスに基づくカスタムQQuickItem を作成することで達成されます。

Scene Graph - Custom QSGRenderNodeの例は、このアプローチのデモンストレーションを提供します。

QPainterを使ったカスタムアイテム

QQuickItem は、QQuickPaintedItem というサブクラスを提供しており、QPainter を使ってコンテンツをレンダリングすることができます。

警告: QQuickPaintedItem を使用すると、ソフトウェア・ラスタライズを使用するか、OpenGLフレーム・バッファ・オブジェクト(FBO)を使用して、コンテンツをレンダリングするために間接的な2Dサーフェスを使用するため、レンダリングは2段階の操作になります。最初にサーフェスをラスタライズし、次にサーフェスを描画する。シーングラフAPIを直接使用する方が、常に大幅に高速です。

ロギング・サポート

シーングラフは多くのロギング・カテゴリーをサポートしています。これらは Qt のコントリビューターに役立つだけでなく、パフォーマンスの問題やバグを追跡するのに役立ちます。

  • qt.scenegraph.time.texture - テクスチャのアップロードに費やされた時間を記録します。
  • qt.scenegraph.time.compilation - シェーダのコンパイルに費やされた時間を記録します。
  • qt.scenegraph.time.renderer - レンダラの様々なステップに費やされた時間を記録します。
  • qt.scenegraph.time.renderloop - レンダリングループの様々なステップに費やされた時間を記録します。 レンダリングループでは、GUI とレンダースレッドの両方で、さまざまなフ レーム準備ステップ間の経過時間を知ることができます。そのため、例えば vsync ベースのスロットリングや () などの低レベルの Qt イネーブラが、レンダリングやプレゼンテーションのパイプラインにどのような影響を与えるかを確認するための、有用なトラブルシューティングツールにもなります。threaded QWindow::requestUpdate
  • qt.scenegraph.time.glyph - 距離フィールドグリフの準備に費やされた時間を記録します。
  • qt.scenegraph.general - シーングラフとグラフィックススタックのさまざまな部分に関する一般的な情報をログに記録します。
  • qt.scenegraph.renderloop - レンダリングに関わるさまざまな段階の詳細なログを作成します。このログモードは、主に Qt で作業する開発者にとって有用です。

レガシーQSG_INFO 環境変数も利用できます。これをゼロ以外の値に設定すると、qt.scenegraph.general カテゴリが有効になります。

注意: グラフィックスの問題に遭遇したときや、どのレンダリングループやグラフィックスAPIが使用されているか疑わしいときは、常に少なくともqt.scenegraph.generalqt.rhi.* を有効にしてアプリケーションを起動するか、QSG_INFO=1 を設定してください。これにより、初期化中にデバッグ出力に重要な情報が表示されます。

シーングラフバックエンド

パブリックAPIに加え、シーングラフには、ハードウェア固有の適応を行うための実装をオープンにする適応レイヤーがあります。これはドキュメント化されていない、内部的でプライベートなプラグインAPIで、ハードウェア適応チームがハードウェアを最大限に活用できるようにする。これには以下が含まれる:

  • カスタムテクスチャ; 特に、QQuickWindow::createTextureFromImage の実装と、ImageBorderImage タイプで使用されるテクスチャの内部表現。
  • カスタムレンダラ:アダプテーションレイヤは、シーングラフをどのようにトラバースしてレンダリングするかをプラグインに決定させるため、特定のハードウェア向けにレンダリングアルゴリズムを最適化したり、パフォーマンスを向上させる拡張機能を利用したりすることができます。
  • テキストやフォントのレンダリングを含む、デフォルトの QML タイプの多くをカスタムシーングラフで実装。
  • カスタムアニメーションドライバ: アニメーションシステムが低レベルディスプレイの垂直リフレッシュにフックすることで、スムーズなレンダリングが可能になります。
  • カスタムレンダーループ: QMLが複数のウィンドウをどのように扱うかをよりよく制御できるようにしました。

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.