Post 54。VK_EXT_descriptor_buffer はデスクリプタ
プール/セット/更新の踊りを完全に取り除く:デスクリプタは自分が
所有する VkBuffer 内の不透明バイトになる。デスクリプタ blob を
memcpy でデバイス可視メモリに書き、2つのコマンドでバインド——
VkDescriptorPool なし、VkDescriptorSet オブジェクトなし、
vkUpdateDescriptorSets なし。
example.sh
$ ./app --headless out.ppm
descriptor blob: 64 bytes at set-offset 0
wrote out.ppm
ライフサイクルの差分
| 従来 | デスクリプタバッファ |
|---|---|
vkCreateDescriptorPool + サイズ推測 | getDescriptorSetLayoutSizeEXT でサイズ決めた vkCreateBuffer |
vkAllocateDescriptorSets | バッファへのオフセット |
vkUpdateDescriptorSets | vkGetDescriptorEXT が base + offset にバイトを書く |
vkCmdBindDescriptorSets | vkCmdBindDescriptorBuffersEXT + vkCmdSetDescriptorBufferOffsetsEXT |
順序通りのセットアップ
main.cpp (device + layout)
vk::PhysicalDeviceDescriptorBufferFeaturesEXT feat{};
feat.setDescriptorBuffer(true);
// `feat` を createDevice にチェイン、有効化
// VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME
dslci.setFlags(eDescriptorBufferEXT) // レイアウトに必須
.setBindings(bindings);
auto dsl = device->createDescriptorSetLayoutUnique(dslci);
vk::DeviceSize setSize =
device->getDescriptorSetLayoutSizeEXT(dsl.get(), dldi);
vk::DeviceSize b0ofs =
device->getDescriptorSetLayoutBindingOffsetEXT(dsl.get(), 0, dldi);main.cpp (buffer + write)
auto descBuf = vk.createBuffer(setSize,
eResourceDescriptorBufferEXT | eSamplerDescriptorBufferEXT |
eShaderDeviceAddress, // デバイスアドレスが必要
eHostVisible | eHostCoherent, true);
vk::DescriptorGetInfoEXT gi{};
vk::DescriptorDataEXT dd{};
dd.setPCombinedImageSampler(&dii); // sampler+view+layout
gi.setType(eCombinedImageSampler).setData(dd);
device->getDescriptorEXT(gi,
props.combinedImageSamplerDescriptorSize, // 実装固有のサイズ!
(char*)descBuf.mapped + b0ofs, dldi); // -> 生バイト
main.cpp (bind + draw)
vk::DescriptorBufferBindingInfoEXT bi{};
bi.setAddress(device->getBufferAddress(descBuf.buf.get()))
.setUsage(eResourceDescriptorBufferEXT | eSamplerDescriptorBufferEXT);
cmd.bindDescriptorBuffersEXT(bi, dldi);
cmd.setDescriptorBufferOffsetsEXT(eGraphics, layout, /*set*/0,
/*bufferIndex*/0, /*offset*/0, dldi);
cmd.draw(3, 1, 0, 0);なぜ重要か
- **テクスチャ交換 = 約64バイトの書き換え。**各タイルのソースが フレーム毎に新しい DMA-Buf インポートのビデオウォールで (post 49)、 フレームインフライト毎にデスクリプタセットスロットを燃やさない—— バイトをパッチしてメモリバリアを押すだけ。
- プールサイズ決め不要:
VkDescriptorPoolSizeの簿記( 「combined-image-sampler はいくつ必要?」)が消える;バッファは 作った大きさ。 - 複数セット = オフセット:バインディングスロット毎の
VkDescriptorSetLayoutは依然として存在(形状を定義)するが、 セットは単なるオフセット——フレーム毎にデスクリプタ領域を リングバッファ化できる。
注意点
- デスクリプタサイズは不透明で実装毎:ハードコード禁止——
常に
VkPhysicalDeviceDescriptorBufferPropertiesEXTからcombinedImageSamplerDescriptorSizeなどを問い合わせる。 eDescriptorBufferEXTはレイアウトとパイプラインの両方に 設定必須(VkPipelineCreateFlagBits::eDescriptorBufferEXT)—— パイプラインフラグを忘れるとバインド時にバリデーションが 文句を言う。- ホスト書き込みにはバリアが必要:GPU が読みうる間に CPU で
書くデスクリプタには
HOST_WRITE → SHADER_READの順序付けが 必要(あるいはサブミット前に書くだけ——ただしリングバッファは そうはいかない)。 - この拡張はデスクトップではどこにでもある(RADV、NVIDIA、ANV)
ただし features の
descriptorBufferを確認——組み込みポート は欠くかもしれない。
完全プログラム
Example: VulkanExamples/p54_descbuf
—main.cpp:デスクリプタバッファ経由でバインドした1つのテクスチャ
→ チェッカーボード PPM、check.py が両色をサンプル。この記事が役に立った場合は、TechOverflow での新しい記事の調査と公開を支援するため、「Buy Me a Coffee」でのご支援、または PayPal 経由でのご寄付をご検討ください