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ça | rol |
|---|---|
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::DispatchLoaderDynamic | EXT 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:
[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
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.pyKontrol
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.
#!/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()$ ./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 nameTam kaynak
// 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.