一方、私のプロジェクトの1つに取り組んでいる間、親ウィンドウにアクセスするわずかな機会なしに、 CALayer(Mac OS X)でQMLシーンを描画する必要に直面しました。 問題の可能な解決策の1週間の検索は、前述のタスクの発生と同時にリリースステータスを受け取った一致が成功したため、Qt 5.4のQQuickRenderControlを使用することが最も適切な解決策であることを示しました。
最初は、タスクは些細で、数晩で解決されると想定していましたが、どれだけ間違えていましたか?
いくつかのポイント
- QQuickRenderControlは、QMLシーンへの変更に関する通知を受信し、反対方向(実際には「松葉杖」)にコマンドを送信するためのQQuickWindow実装への追加インターフェイスです。
- レンダリング結果はQOpenGLFramebufferObject(以降FBO)の形式で取得され、後でテクスチャとして使用できます。
- それぞれQuickWindowを直接操作する必要があります。QQuickViewが提供するQMLロードサービスは利用できず、自分で実装する必要があります。
- ウィンドウは実際には作成されないため、マウスとキーボードのイベントをQQuickWindowに人為的に転送する必要があります。 ウィンドウのサイズを手動で制御することも必要です。
- 私はQQuick 5.4でQQuickRenderControlを1つだけ使用する例を見つけることができました(例\ Qt-5.4 \クイック\レンダリングコントロール)-実際にはすべての手順が実行されました。
元の問題を解決するには何をする必要がありますか?
1)FBOでレンダリングし、QQuickRenderControlを介してこのプロセスを制御するためのQQuickWindow設定を実装します。
2)Qmlの読み込みを実装し、結果をQQuickWindowに添付します。
3)マウスおよびキーボードイベントの転送を実装します。
4)FBOを描画します(すべてに対応していました)。
この記事では、パラグラフ1)、後続のパートの残りのパラグラフのみに専念できるようにします(これがおもしろければ)。
QQuickWindowをカスタマイズする
外部QOpenGLContext
開始点は、FBOが最終的に描画されるOpenGLコンテキストです。 しかし、高い確率で、最初はQtとは関係のないコンテキストで作業する必要があるため、コンテキストをオペレーティングシステムの形式からQOpenGLContextのインスタンスに変換する必要があります。 これを行うには、 QOpenGLContext :: setNativeHandleメソッドを使用します。
NSOpenGLContextベースの使用例:
NSOpenGLContext* nativeContext = [super openGLContextForPixelFormat: pixelFormat]; QOpenGLContext* extContext = new QOpenGLContext; extContext->setNativeHandle( QVariant::fromValue( QCocoaNativeContext( nativeContext ) ) ); extContext->create();
使用可能なネイティブコンテキストのリストは、Qtヘッダーファイル(\ QtPlatformHeadersを含む)で直接表示するのが最適です。 このパートのドキュメントは完全ではありません。
さらに、このコンテキストを使用できます(ただし同時に、このコンテキストの状態の変更が所有者の操作と競合しないように注意深く監視する必要があります)が、共有コンテキストを作成できます。
QSurfaceFormat format; format.setDepthBufferSize( 16 ); format.setStencilBufferSize( 8 ); context = new QOpenGLContext; context->setFormat( format ); context->setShareContext( extContext ); context->create();
QMLでOpenGLコンテキストを使用するための重要なニュアンスは、構成された深度バッファーとステンシルバッファーの存在です。そのため、元のコンテキストのパラメーターに影響を与えることができない場合は、「深度バッファーサイズ」と「ステンシルバッファーサイズ」が設定された共有コンテキストを使用する必要があります。
QQuickWindowの作成
QQuickWindowを作成するとき、QQuickRenderControlが最初に作成され、コンストラクターに渡されます。
QQuickRenderControl* renderControl = new QQuickRenderControl(); QQuickWindow* quickWindow = new QQuickWindow( renderControl ); quickWindow->setGeometry( 0, 0, 640, 480 );
さらに、FBOの作成をさらに成功させるには、ウィンドウサイズを設定することが重要です。
QQuickRenderControlおよびQOpenGLFramebufferObjectの初期化
QQuickRenderControl :: initializeを呼び出す前に、コンテキストを最新にすることが重要です。 呼び出し中に、sceneGraphInitialized信号が生成されます。これは、FBOを作成するのに適したポイントです(そのため、現在のコンテキストを公開する必要があります)。
QOpenGLFramebufferObject* fbo = nullptr; connect( quickWindow, &QQuickWindow::sceneGraphInitialized, [&] () { fbo = new QOpenGLFramebufferObject( quickWindow->size(), QOpenGLFramebufferObject::CombinedDepthStencil ); quickWindow->setRenderTarget( fbo ); } ); offscreenSurface = new QOffscreenSurface(); offscreenSurface->setFormat( context->format() ); offscreenSurface->create(); context->makeCurrent( offscreenSurface ); renderControl->initialize( context ); context->doneCurrent();
レンダリング
レンダリングは、信号QQuickRenderControl :: renderRequestedおよびQQuickRenderControl :: sceneChangedへの反応として行う必要があります。 これら2つのケースの違いは、2番目のケースでは、QQuickRenderControl :: polishItemsおよびQQuickRenderControl :: syncを追加で呼び出す必要があることです。 2番目の重要な機能は、上記のシグナルのハンドラーで直接レンダリングしないことを強くお勧めします。 したがって、間隔が短いタイマーが使用されます。 さて、最後の微妙な点は、共有OpenGLコンテキストを使用する場合、レンダリング後にglFlushを呼び出す必要があることです。それ以外の場合、プライマリコンテキストはFBOの変更を認識しません。
bool* needSyncAndPolish = new bool; *needSyncAndPolish = true; QTimer* renderTimer = new QTimer; renderTimer->setSingleShot( true ); renderTimer->setInterval( 5 ); connect( renderTimer, &QTimer::timeout, [&] () { if( context->makeCurrent( offscreenSurface ) ) { if( *needPolishAndSync ) { *needPolishAndSync = false; renderControl->polishItems(); renderControl->sync(); } renderControl->render(); quickWindow->resetOpenGLState(); context->functions()->glFlush(); context->doneCurrent(); } ); connect( renderControl, &QQuickRenderControl::renderRequested, [&] () { if( !renderTimer->isActive() ) renderTimer->start(); } ); connect( renderControl, &QQuickRenderControl::sceneChanged, [&] () { *needPolishAndSync = true; if( !renderTimer->isActive() ) renderTimer->start(); } );
まあ、それは基本的にそれです、タスクの最初の部分は完了しました。
上記の概念を実装するクラスは、GitHubで入手できます: FboQuickWindow.h 、 FboQuickWindow.cpp
コメント、コメント、コメント内の健全な批判を歓迎します。
続き: パートII:QMLのダウンロード 、 パートIII:ユーザー入力の処理