DMA-Buf をインポートした(external_memory_dma_buf の記事)。しかし
デコーダーは自分のキューで、異なるドライバー内でそれに書き込んだ。
Vulkan のメモリモデルでは、書き込みは所有権を移転しない限り、
書き込みを行ったキューファミリにのみ可視だ。
VK_EXT_queue_family_foreign は、センチネルのファミリインデックス
VK_QUEUE_FAMILY_FOREIGN_EXT —「最後に触れた側」のキュー — と、
バッファを自分のキューファミリに移すセマフォ/バリアの仕組みを提供する。
問題
problem.txt
デコーダーキューが DMA-Buf に書き込む → 自分のグラフィックスキューがサンプルする
↑ Vulkan のメモリモデルでは異なる
「キューファミリ」 —
書き込みの可視性は保証されない転送なしのサンプリングは未定義動作だ。運よく動くこともあれば、 古いデータを返すこともある。
所有権の転送
transfer.cpp
// デコーダー側(概念的 — エクスポーターがセマフォをシグナルするか、
// 完了を知る方法がある):
// DMA-Buf は VK_QUEUE_FAMILY_FOREIGN_EXT に「所有」されている
// 自分のキュー — release/acquire ペア、または両フィールドを持つバリア:
vk::ImageMemoryBarrier imb{};
imb.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT; // 以前:foreign
imb.dstQueueFamilyIndex = myGraphicsFamily; // 以後:自分
imb.image = importedImg;
imb.oldLayout = vk::ImageLayout::eGeneral;
imb.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
// srcAccess = foreign 側が行ったもの — 多くは同期を運ぶ
// セマフォ wait 経由の eMemoryWrite
cmd.pipelineBarrier(eTopOfPipe, eFragmentShader, {}, {}, {}, {imb});srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT がポイントだ。
これは「どのファミリが書き込んだか分からないので、取得する」という
意味だ。エクスポーターの完了を表すセマフォ wait と組み合わせれば、
書き込みが可視になる。
セマフォの経路
sem.cpp
// デコーダーがシグナル。こちらは wait + acquire:
vk::SubmitInfo si{};
si.waitSemaphoreCount = 1;
si.pWaitSemaphores = &decoderSem; // VA-API/V4L2 のフェンスから
si.pWaitDstStageMask = &fragmentStage;
// … 上記のバリアは同じコマンドバッファに入れる …
queue.submit(si);実際には VAAPI/V4L2 は sync fd 経由で完了をシグナルし、それが
VkSemaphore になる。vkGetSemaphoreFdKHR/vkImportSemaphoreFdKHR
で取り込む。
全体の流れ
flow.txt
FFmpeg のデコード完了 → AVDRMFrameDescriptor がプレーンの fd を持つ
→ DMA-Buf パスで VkImage としてインポート
→ バリア:FOREIGN_EXT → グラフィックスキュー、eGeneral → eShaderReadOnly
→ グリッドシェーダーでサンプリング
→ フレーム退役時:返却(dst=FOREIGN_EXT)するか、VkImage をそのまま
破棄 — DMA-Buf はデコーダーのプールに戻る実践的なルール
VK_QUEUE_FAMILY_FOREIGN_EXTは擬似ファミリだ。queueFamilyPropertiesには決して現れない。srcQueueFamilyIndex/dstQueueFamilyIndexフィールドにだけ使う。- バリア単体では順序付けは運ばれない。セマフォ wait と組み合わせて、 自分の読み取りが始まる前に foreign 側の書き込みが完了している ことを保証する。
- 使い終わったら release する。
dst=FOREIGN_EXTのバリアはバッファを 返す。DMA-Buf が再利用ではなく破棄される場合に限り省略できる。 - ドライバーとハードウェアが実際に同じキューファミリを共有する同一 デバイスのインポートでは、バリアは no-op だ。ただし仕様上は宣言が 必要だ。
- これを怠ると、症状は断続的な古いフレームになる。典型的な 「最初のフレームは緑、以降のフレームは正常」という動画インポートの バグだ。
この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください