VK_KHR_dynamic_rendering_local_read で同じレンダーパス内のカラーアタッチメントを読む方法

Post 55。サブパス入力アタッチメント(「書き込んでいるピクセルを 読む」)はかつて VkRenderPass/VkSubpass オブジェクトを必要と した。VK_KHR_dynamic_rendering_local_read は同じパターンを vkCmdBeginRendering にもたらす:1つのイメージがカラー アタッチメントと入力アタッチメントを同時に兼ね、フラグメントは subpassLoad() で自分のピクセルを読む。タイルベース GPU では データはタイルメモリから出ない;どこでもブレンドモード、蓄積 エフェクト、追加パスなしのマルチステージポストエフェクトの背後に あるパス内フィードバックプリミティブだ。

デモ:draw 1 がグラデーションを書く → by-region バリアが アタッチメントを eRenderingLocalRead に反転 → draw 2 が自分の ピクセルを読み反転を書く——同じイメージ、同じレンダーパス。

材料

main.cpp (feature + image)
vk::PhysicalDeviceDynamicRenderingLocalReadFeaturesKHR lr{};
lr.setDynamicRenderingLocalRead(true);
// デバイス作成にチェイン + 有効化
// VK_KHR_DYNAMIC_RENDERING_LOCAL_READ_EXTENSION_NAME

// イメージは両方の usage が必要:
ici.setUsage(eColorAttachment | eInputAttachment | eTransferSrc);

デスクリプタタイプは eInputAttachment——サンプラは関わらない (subpassLoad はテクセルを読む、フィルタしない):

main.cpp (reader shader)
layout(input_attachment_index = 0, binding = 0)
    uniform subpassInput prev;
void main() {
    vec4 p = subpassLoad(prev);   // このフラグメント自身のピクセル
    outColor = vec4(1.0 - p.rgb, 1.0);
}

by-region バリア——重要な1行

書き込み draw と読み取り draw の間、同じ beginRendering 内で:

main.cpp (in-pass barrier)
vk::ImageMemoryBarrier2 mid{};
mid.setOldLayout(vk::ImageLayout::eColorAttachmentOptimal)
   .setNewLayout(vk::ImageLayout::eRenderingLocalRead)  // ← 魔法
   .setSrcStageMask(eColorAttachmentOutput)
   .setSrcAccessMask(eColorAttachmentWrite)
   .setDstStageMask(eFragmentShader | eColorAttachmentOutput)
   .setDstAccessMask(eInputAttachmentRead | eColorAttachmentWrite);
vk::DependencyInfo dep{};
dep.setDependencyFlags(vk::DependencyFlagBits::eByRegion)
   .setImageMemoryBarriers(mid);
cmd.pipelineBarrier2(dep);

eByRegion がこれをタイル安全にする:順序付けは同じピクセルを 覆うフラグメント間でのみ必要——それはタイル GPU がフラッシュなしで 保証できるものそのもの。

同様に重要:beginRendering に渡す VkRenderingAttachmentInfo::imageLayout は draw 1 では eColorAttachmentOptimal;local-read セクションでは eRenderingLocalRead に更新——そしてデスクリプタも eRenderingLocalRead を宣言しなければならない(描画時の レイアウトを記述する)。

main.cpp (descriptor layout)
vk::DescriptorImageInfo dii{{}, view,
    vk::ImageLayout::eRenderingLocalRead};  // eShaderReadOnly ではない!

VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR は特殊:入力 アタッチメント読み取りとカラーアタッチメント書き込みの両方を 許可——それがポイント全体、フラグメントが自分のテクセルを 読み-修正-書きする。

使うべきとき

  • ピクセル毎の読み-修正-書きパターン(カスタムブレンディング、 カラーキー合成)——さもなければ2パス + VRAM 経由の完全イメージ 往復が必要。
  • タイル GPU 上:レンダーパスオブジェクトなしのサブパス式 アルゴリズム(遅延マージ、半透明)。
  • 隣接読み取りには向かない:subpassLoad はフラグメント 自身のピクセルのみ読む——畳み込み/ブラーには本物のイメージ バリアが必要。

注意点

  • subpassLoad(prev)——texture() ではない。入力アタッチメントは サンプラを完全にバイパス;フレームバッファへのインデックスは 暗黙。
  • 複数アタッチメントには vkCmdSetRenderingAttachmentLocations/InputAttachmentIndices が必要;単一アタッチメントならデフォルトで十分(このデモ)。
  • eByRegion は同一キュー・同一レンダーパスを要求——レンダー パスを跨ぐならリージョンバリアではなく完全なパイプライン バリアが必要。
  • フィーチャを確認:dynamicRenderingLocalRead——デスクトップでは 普遍的、タイル GPU でのパターン正確性に必要。

完全プログラム

Example: VulkanExamples/p55_localread

— main.cpp:グラデーション → パス内反転 → PPM、check.py が隅が 書き込まれたグラデーションの反転であることを検証(約300行)。


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