VK_EXT_external_memory_host 解説:ホストメモリを Vulkan にインポートする

VK_EXT_external_memory_dma_buf は GPU↔GPU 共有をカバーする。 しかしデータがすでにホストメモリにある場合はどうか—— mmap されたファイル、V4L2 USERPTR キャプチャバッファ、 自分で埋める共有メモリリング?VK_EXT_external_memory_host は 既存の CPU ポインタを VkDeviceMemory としてインポートする。 ゼロコピーアップロード、ステージングバッファなし。

アイデア

path.txt
従来:     host ptr → memcpy → ステージング VkBuffer → cmd copy → VkImage
インポート:host ptr ──────────────────► VkDeviceMemory ──► VkImage
           (GPU がホスト割り当てを直接サンプル)

要件とプローブ

probe.cpp
// デバイス拡張:VK_EXT_external_memory_host
// (+ VK_KHR_external_memory は通常すでに有効)

// どのホストポインタがインポートできるか?mmap 前に聞く:
vk::PhysicalDeviceExternalMemoryHostPropertiesEXT hostProps{};
// (vkGetPhysicalDeviceProperties2 にチェーン)
hostProps.minImportedHostPointerAlignment; // 多くの場合 4096 = ページサイズ

// このポインタはインポート可能か?
vk::MemoryHostPointerPropertiesEXT mpp{};
dev.getMemoryHostPointerPropertiesEXT(
    vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
    ptr, &mpp);
// mpp.memoryTypeBits — どの vkAllocateMemory タイプが受け付けるか

ポインタは minImportedHostPointerAlignment に整列しなければ ならない。通常 getpagesize()。malloc したメモリではだめで、 posix_memalign か mmap なら使える。

インポート

import.cpp
vk::ImportMemoryHostPointerInfoEXT imp{};
imp.handleType = vk::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT;
imp.pHostPointer = ptr;        // メモリが破棄されるまで有効でなければならない

vk::MemoryAllocateInfo ai{};
ai.pNext              = &imp;
ai.allocationSize     = size;
ai.memoryTypeIndex    = pickFrom(mpp.memoryTypeBits);
auto mem = dev.allocateMemoryUnique(ai);

// リニア VkImage か VkBuffer にバインド——GPU があなたのバイトを読む。

インポートされたホストメモリでの vkMapMemory は概念的に no-op だ。「マップ」はすでに持っているポインタ。コヒーレントな ホスト可視メモリと同様、書き込み前に vkInvalidateMappedMemoryRanges、 後に vkFlushMappedMemoryRanges を呼ぶだけだ。

特に有効な場面

  • V4L2 USERPTR キャプチャ:m.userptr がインポートした範囲を 指す VIDIOC_QBUF → カメラが GPU アドレス可能メモリに 直接 DMA する。
  • mmap されたフレームファイル:ディスクからマップされた プリレンダーテクスチャリング。read+staging+free サイクルなし。
  • 共有メモリ IPC:プロデューサーが shm にピクセルを書き、 Vulkan コンシューマーが直接サンプルする。unix ソケット バッファコピーなし。
  • FFmpeg SW フレーム:posix_memalign したバッファにデコードし、 インポートし、1回の vkCmdCopyBufferToImage でアップロード。 ステージング割り当てをスキップする。
vs.txt
vs ステージングバッファ:  -1 割り当て、フレームあたり -1 memcpy、cmd 1つ減
vs DMA-Buf インポート:    fd 不要 — ホストメモリ、デバイス BO ではない
vs HOST_VISIBLE イメージ: メモリ寿命をあなたが制御、ドライバーではない

実践的なルール

  • ポインタは VkDeviceMemory より長生きしなければならない。 早く free するのは GPU 上の use-after-free であり、キャッチ できる segfault ではない。
  • minImportedHostPointerAlignment が契約だ。 posix_memalign(&p, 4096, size) か mmap(NULL, …, MAP_PRIVATE…) で整列する。
  • すべてのメモリタイプがホストインポートを受け付けるわけではない。 任意の割り当てと同様に memoryTypeBits をフィルタする。一部の ドライバーでは HOST_VISIBLE タイプのみが適格。
  • タイル化/optimal イメージはリニアホストメモリでバックできない。 VkBuffer にインポートして1回の vkCmdCopyBufferToImage を するか、VK_EXT_host_image_copy でリニアレイアウトを書き込む。
  • キャッシュコヒーレンシーはあなたの仕事:CPU 書き込み後、 サブミット前に vkFlushMappedMemoryRanges。GPU はあなたの キャッシュをスヌープしない。

同じカテゴリの類似記事をチェック: Vulkan C++