VK_EXT_memory_budget で Vulkan の GPU メモリバジェットとライブヒープ使用量をクエリする方法

Example: VulkanExamples/p42_budget

シリーズ post 42(post 28:ケイパビリティクエリ、post:メモリタイプ)。

VkPhysicalDeviceMemoryProperties はヒープのサイズを教える—— 箱に印刷された16GB。教えないのは:そのヒープのどれだけが 既に消えているか(他のアプリ、コンポジタ、自分の以前の アロケーション)、そしてドライバがいくらまで与えてくれるか。 VK_EXT_memory_budget は1つのチェーンした構造体で両方の穴を 塞ぐ:

main.cpp (excerpt)
vk::PhysicalDeviceMemoryBudgetPropertiesEXT budget{};
vk::PhysicalDeviceMemoryProperties2 mp{};
mp.pNext = &budget;
phys.getMemoryProperties2(&mp);

for (uint32_t i = 0; i < mp.memoryProperties.memoryHeapCount; ++i)
    printf("heap%u size=%lluMB usage=%lluMB budget=%lluMB\n", i,
           (unsigned long long)(mp.memoryProperties
                                    .memoryHeaps[i].size >> 20),
           (unsigned long long)(budget.heapUsage[i] >> 20),
           (unsigned long long)(budget.heapBudget[i] >> 20));
  • heapBudget[i]——ドライバが現在アロケートを許す量(ヒープ サイズ − ドライバ予約 − 他のプロセス)。
  • heapUsage[i]——今実際にアロケートされている量。

テスト Radeon 上:

example.sh
--- before allocations ---
heap0 size=16128MB usage=0MB budget=15124MB flags=0x1   # VRAM
heap1 size=15540MB usage=0MB budget=15339MB flags=0x0   # GTT/host
heap2 size=256MB   usage=0MB budget=54MB   flags=0x1    # BAR carve-out
--- after 3x256MB device-local ---
heap0 size=16128MB usage=768MB budget=15124MB flags=0x1

2つの教訓的な詳細:

  • budget ≠ size:heap0 の 16GB は 15.1GB のバジェットを 示す——ドライバは ~1GB を自分用に取っておく(ページテーブル、 ファームウェア、他のクライアント)。heap2 はリサイズ可能 BAR ウィンドウ:256MB ヒープなのにバジェットは 54MB のみ、つまり 大半は恒久的に予約済み。
  • usage はあなた以外も数える:共有ヒープ上の heapUsage は システム全体——他のアプリが VRAM をアロケートするとここに 現れる。だからこそアプリはヒープサイズを信じる代わりに、 キャッシュを追い出す・増やすときにバジェットを再確認すべき。

チェック

check.py
# SPDX-License-Identifier: CC0-1.0
import re, subprocess

o = subprocess.run(["./app"], check=True, capture_output=True,
                   text=True).stdout

def parse(text):
    return {i: (int(u), int(b)) for i, u, b in re.findall(
        r"heap(\d+) size=\d+MB usage=(\d+)MB budget=(\d+)MB "
        r"flags=0x1", text)}

before, _, after = o.partition("--- after 3x256MB device-local ---")
heaps_b = parse(before)
heaps_a = parse(after)
assert heaps_b and heaps_a, o
grew = [i for i in heaps_a
        if heaps_a[i][0] - heaps_b.get(i, (0, 0))[0] >= 700]
assert grew, f"no heap grew by ~768MB: {o}"
print("OK: heap budget/usage reported, device-local usage +768MB")
example.sh
$ python3 check.py
OK: heap budget/usage reported, device-local usage +768MB

やってはいけない使い方

  • バジェットを約束として扱うな——クエリと vkAllocateMemory の間で縮みうる(他のアプリがメモリを掴んだ)。依然 eOutOfDeviceMemory をハンドルせよ;バジェットはポリシーの 入力であり予約ではない。
  • バジェットを合計するな——ヒープは奇妙に重なる(BAR ヒープ は VRAM へのウィンドウ);フラグで正しいヒープを選べ。
  • フレーム毎に再クエリするな——これはシステムコールに 近いクエリ;ドロー毎ではなく、アロケーション圧力または タイマーでポーリングせよ。

このシリーズのすべてのソースコードは CC0 1.0 Universal (CC0-1.0) の下でリリース——パブリックドメイン献身、帰属 表示不要。


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