Vulkan validation layer'ları nasıl etkinleştirilir ve troubleshooting için debug label'lar nasıl kullanılır

Example: VulkanExamples/p18-validation

Bu seride karşılaştığın her sessiz çöküş, siyah ekran veya “NVIDIA’da çalışıyor ama AMD’de değil” durumu, VK_LAYER_KHRONOS_validation ve VK_EXT_debug_utils bağlandığında dakikalar içinde teşhis edilebilir. Demo, raporlama zincirini uçtan uca kanıtlamak için bilerek bir API suçu işler (TRANSFER_DST olmayan bir buffer’a vkCmdFillBuffer).

Üç bağımsız parça

parçarol
VK_LAYER_KHRONOS_validation (instance layer)çağrıları yakalar, spec’e karşı kontrol eder, hataları VUID’lerle raporlar
VK_EXT_debug_utils (instance ext)kanal: messenger callback + nesne isimlendirme + command-buffer label’ları
vk::detail::DispatchLoaderDynamicEXT fonksiyonları libvulkan tarafından export edilmez — vkGetInstanceProcAddr ile çözülür ve her EXT çağrısı dinamik dispatcher’ı sondaki argüman olarak alır

Layer bir paket olarak kurulur (apt install vulkan-validationlayers veya Vulkan SDK). Sürücünün parçası değildir — yokken istersen ErrorLayerNotPresent alırsın; bu yüzden production kodu layer’ı #ifdef DEBUG veya bir env flag’in arkasına koyar.

Her özellik sana ne kazandırır

Messenger layer çıktısını stderr yerine callback’ine yönlendirir — filtrelenebilir, loglanabilir, testlerde assert edilebilir:

example.cpp
[validation:Error] vkCmdFillBuffer(): dstBuffer
  (VkBuffer 0x30000000003[demo-buffer]) was created with
  VK_BUFFER_USAGE_2_VERTEX_BUFFER_BIT but requires
  VK_BUFFER_USAGE_2_TRANSFER_DST_BIT.
  (...#VUID-vkCmdFillBuffer-dstBuffer-00029)

[demo-buffer]‘a dikkat — bu nesne isimlendirme (vkSetDebugUtilsObjectNameEXT). Buffer’la ilgili gelecekteki her mesaj ham handle yerine ismi basar; RenderDoc ve Nsight da kaynak listesinde gösterir.

Debug label’lar (vkCmdBeginDebugUtilsLabelEXT) command- buffer bölgelerini köşeli ayraçlarla işaretler — profiler’larda ve validation callback’inin kendi bölge bağlamında görünür, yani bir mesaj sana frame’inin hangi parçasının ürettiğini söyler.

Build & çalıştırma

build.sh
apt install vulkan-validationlayers   # Debian/Ubuntu
g++ -std=c++23 -O2 main.cpp -o app -lvulkan -lglfw -lshaderc
./app           # yakalanan hatayı + VUID'sini basar
python3 check.py

Kontrol

check.py çıktıda dört şey assert eder: temiz çıkış, bir Error-severity mesajı, bir VUID referansı ve — nesne isimlendirmenin çalıştığını kanıtlamak için — hata metni içinde demo-buffer string’i.

check.py
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
# check.py — validation-layer demosunu çalıştır ve doğrula:
#   1. uygulama tamamlanıyor (exit 0)
#   2. validation layer kasıtlı
#      fillBuffer-without-TRANSFER_DST hatasını gerçekten YAKALADI
#   3. hata mesajı isimlendirilmiş nesnemiz "demo-buffer"dan bahsediyor
import os
import re
import subprocess
import sys


def main():
    exe = sys.argv[1] if len(sys.argv) > 1 else "./app"
    env = dict(os.environ)
    # validation layer lokal olarak açıldıysa (vl-local/) onu kullan;
    # değilse sistem kurulumuna güven (vulkan-validationlayers)
    here = os.path.dirname(os.path.abspath(__file__))
    layerdir = os.path.join(here,
        "vl-local/usr/share/vulkan/explicit_layer.d")
    libdir = os.path.join(here, "vl-local/usr/lib/x86_64-linux-gnu")
    if os.path.isdir(layerdir):
        env["VK_ADD_LAYER_PATH"] = layerdir
        env["LD_LIBRARY_PATH"] = \
            libdir + ":" + env.get("LD_LIBRARY_PATH", "")
    r = subprocess.run([exe], capture_output=True, text=True,
                       timeout=60, env=env)
    out = r.stdout + r.stderr
    print(out, end="")
    assert r.returncode == 0, f"exit {r.returncode}"

    # layer bir Error-severity mesajı üretmiş olmalı
    assert "[validation:Error]" in out, \
        "no validation error reported — is the layer installed?"
    # fillBuffer usage için VUID: CmdFillBuffer-apiVersion-07830
    # (ifade değişir; usage-flag şikayeti + VUID kontrol et)
    assert "VUID" in out, "no VUID in validation output"
    assert "TRANSFER" in out.upper(), "expected usage-flag complaint"
    # nesne isimlendirme geçmiş olmalı
    assert "demo-buffer" in out, "named object not in message"
    print("OK: validation layer caught the deliberate error, "
          "with VUID + object name")


if __name__ == "__main__":
    main()
output.txt
$ ./app
device: AMD Radeon RX 7800 XT (RADV NAVI32)
[demo] buffer named 'demo-buffer'
[demo] calling fillBuffer without TRANSFER_DST usage...
[validation:Error] vkCmdFillBuffer(): dstBuffer
    (VkBuffer 0x30000000003[demo-buffer]) was created with
    VK_BUFFER_USAGE_2_VERTEX_BUFFER_BIT but requires
    VK_BUFFER_USAGE_2_TRANSFER_DST_BIT. [...VUID...00029]
[demo] done
OK: validation layer caught the deliberate error, with VUID + object name

Tam kaynak

main.cpp
// SPDX-License-Identifier: CC0-1.0
//
// Post 18: validation layer'lar + VK_EXT_debug_utils. Validation
// layer API kötüye kullanımını çalışma zamanında yakalar;
// debug-utils uzantısı bu mesajları SENİN callback'ine yönlendirir
// ve nesnelere ile command-buffer bölgelerine isim/label eklemeni
// sağlar. Demo tüm zinciri kanıtlamak için bilerek BİR validation
// hatası tetikler (TRANSFER_DST usage'ı olmayan buffer'a
// fillBuffer).
//
//   ./app     layer mesajlarını + bir debug label basar; check.py assert eder
//
// Build:  g++ -std=c++23 -O2 main.cpp -o app -lvulkan -lglfw -lshaderc

#include "vkmini.hpp"

#include <GLFW/glfw3.h>

/// Debug callback imzası spec ile sabit. Kompakt bir satır basıyoruz;
/// check.py onu grepliyor.
static VKAPI_ATTR VkBool32 VKAPI_CALL
debugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT severity,
              vk::DebugUtilsMessageTypeFlagsEXT type,
              const vk::DebugUtilsMessengerCallbackDataEXT* data,
              void*) {
    printf("[validation:%s] %s\n",
           vk::to_string(severity).c_str(), data->pMessage);
    return VK_FALSE; // suçlu çağrıyı iptal etme
}

int main() {
    vkmini::Vk vk;

    // ---- validation layer + debug_utils İLE instance --------------
    // Layer bir *layer* (yakalanan çağrılar); messenger bir *uzantı*
    // (raporlama kanalı). İkisi de lazım.
    vk::ApplicationInfo ai{};
    ai.setPApplicationName("vkmini-p18")
        .setApiVersion(VK_API_VERSION_1_3);
    const char* layers[] = {"VK_LAYER_KHRONOS_validation"};
    const char* exts[] = {VK_EXT_DEBUG_UTILS_EXTENSION_NAME};
    vk::InstanceCreateInfo ici{};
    ici.setPApplicationInfo(&ai)
        .setPEnabledLayerNames(layers)
        .setPEnabledExtensionNames(exts);
    vk.instance = vk::createInstanceUnique(ici);

    // ---- dinamik dispatch -----------------------------------------
    // EXTENSION fonksiyonları libvulkan tarafından export edilmez —
    // vkGetInstanceProcAddr ile çözülmeliler. Vulkan-Hpp bunu
    // DispatchLoaderDynamic'e sarar: her EXT çağrısı onu sondaki
    // argüman olarak alır.
    vk::detail::DispatchLoaderDynamic dldi(
        vk.instance.get(), vkGetInstanceProcAddr);

    // ---- messenger: instance etrafında oluşturulur VE yok edilir --
    vk::DebugUtilsMessengerCreateInfoEXT mi{};
    mi.setMessageSeverity(vk::DebugUtilsMessageSeverityFlagBitsEXT::
                              eWarning |
                          vk::DebugUtilsMessageSeverityFlagBitsEXT::
                              eError)
        .setMessageType(vk::DebugUtilsMessageTypeFlagBitsEXT::
                            eGeneral |
                        vk::DebugUtilsMessageTypeFlagBitsEXT::
                            eValidation |
                        vk::DebugUtilsMessageTypeFlagBitsEXT::
                            ePerformance)
        .setPfnUserCallback(debugCallback);
    auto messenger = vk.instance
                         ->createDebugUtilsMessengerEXTUnique(
                             mi, nullptr, dldi);

    vk.pickPhysicalDevice();
    vk.createDevice({});

    // ---- nesne isimlendirme: bundan sonraki her mesajda görünür ---
    auto buf = vk.createBuffer(
        16,
        vk::BufferUsageFlagBits::eVertexBuffer, // NOT: TRANSFER_DST yok
        vk::MemoryPropertyFlagBits::eDeviceLocal);
    vk::DebugUtilsObjectNameInfoEXT nameInfo{};
    nameInfo.setObjectType(vk::ObjectType::eBuffer)
        .setObjectHandle(
            reinterpret_cast<uint64_t>(VkBuffer(buf.buf.get())))
        .setPObjectName("demo-buffer");
    vk.device->setDebugUtilsObjectNameEXT(nameInfo, dldi);
    printf("[demo] buffer named 'demo-buffer'\n");

    // ---- kasıtlı hata ---------------------------------------------
    // fillBuffer TRANSFER_DST usage ister; buffer'da yok. Layer
    // olmadan: tanımsız davranış. Onunla: 'demo-buffer'ı isimleyen
    // kesin bir VUID mesajı.
    vk.oneTime([&](vk::CommandBuffer c) {
        // debug label'lar araçlarda (RenderDoc, Nsight) ve bu
        // callback'in kendi çağrı yığını dökümlerinde bölgeleri
        // köşeli ayraçlarla işaretler.
        vk::DebugUtilsLabelEXT label{};
        label.setPLabelName("intentional-mistake-region");
        label.setColor({0.9f, 0.4f, 0.1f, 1.f});
        c.beginDebugUtilsLabelEXT(label, dldi);

        printf("[demo] calling fillBuffer without TRANSFER_DST "
               "usage...\n");
        c.fillBuffer(buf.buf.get(), 0, 4, 0xdeadbeef);

        c.endDebugUtilsLabelEXT(dldi);
    });

    // ---- temizlik -------------------------------------------------
    // Messenger instance'dan ÖNCE yok edilir (vk'daki Unique handle
    // sırası instance'ı en sona bırakır).
    printf("[demo] done\n");
    return 0;
}

Bu serideki tüm kaynak kodu CC0 1.0 Universal (CC0-1.0) altında yayınlandı — kamu malı adaması, atıf gerekmez.


Check out similar posts by category: C++ Vulkan