Render döngünüz önden gitmek ister: girdi örnekle, submit et,
tekrarla. Ama önden kuyruklanan her frame gecikmedir — T’de
örneklenen girdi, kuyrukta iki frame beklediği için ekrana
T+2·refresh’te ulaşır. Çözüm frame pacing: frame N’in
başlangıcını ekranın neredeyse ihtiyaç duyduğu ana kadar geciktirin.
Bunu taşınabilir yapan iki API: VK_KHR_present_wait
(+ VK_KHR_present_id) ve NVIDIA’da VK_NV_low_latency2 (Reflex
marker’larının Vulkan eşdeğeri).
Sorun
pacing yok: submit0 submit1 submit2 submit3 → her biri sırasını bekler
girdi <frames in flight> × refresh kadar bayat
pacing'li: waitForPresent(prev) → girdi örnekle → submit → present
taramadan ~0 frame önce örneklenen girdi — minimum lagVK_KHR_present_wait
// Cihaz: VK_KHR_present_id + VK_KHR_present_wait (+ presentWait özelliği)
uint64_t nextId = 1;
// Her present'i bir id ile etiketle:
vk::PresentIdKHR ids{};
ids.swapchainCount = 1;
ids.pPresentIds = &nextId;
vk::PresentInfoKHR pi{};
pi.pNext = &ids;
queue.presentKHR(pi);
// Sonraki frame: o present gerçekten ekrana ulaşana kadar blokla
// (veya timeout — render döngüsünde asla sınırsız bekleme):
dev.waitForPresentKHR(swapchain, nextId, 16'666'667); // 60Hz'de ~1 refresh
// şimdi: girdi örnekle, frame kur, submit — mümkün olduğunca geç
++nextId;present_wait render edildi‘yi değil sunuldu‘yu bekler —
fence’lerin göremediği kompozitör + tarama gecikmesini emer.
presentWait2‘nin wait-başına flag’lerine ihtiyacınız varsa
VK_KHR_present_wait2/VkPresentWait2InfoKHR ekleyin.
VK_NV_low_latency2 — ‘doğru ana kadar uyu’ API’si
// 1. Opt in: swapchain flag'i eCreatePresentWait? hayır —
// VK_SWAPCHAIN_CREATE_PRESENT_WAIT_BIT present_wait için;
// low_latency2 için sadece cihaz uzantısı + özelliği açın:
vk::LatencySleepModeInfoNV mode{};
mode.lowLatencyMode = VK_TRUE;
mode.lowLatencyBoost = VK_TRUE; // sürücü saatleri boost edebilir
mode.minimumIntervalUs = 0; // veya µs cinsinden frame-rate sınırı
dev.setLatencySleepModeNV(swapchain, mode);
// 2. Her frame'den önce — sürücü doğru
// başlangıç anını belirleyene kadar uyu, bitince semaphore sinyalle:
vk::LatencySleepInfoNV slp{};
slp.signalSemaphore = sema;
slp.value = frameNum; // timeline değeri
dev.latencySleepNV(swapchain, slp);
// 3. Sürücünün gecikme hesabı için frame sınırlarını işaretle:
vk::SetLatencyMarkerInfoNV m{};
m.presentID = frameNum;
m.marker = vk::LatencyMarkerNV::eSimulationStart;
dev.setLatencyMarkerNV(swapchain, m); // sim'den önce
// queue submit'ten sonra eRendersubmitEnd, present'ten sonra ePresentEnd
// 4. Frame başına gecikme dökümünü geri oku:
vk::GetLatencyMarkerInfoNV g{};
g.timingCount = 64;
std::vector<vk::LatencyTimingsFrameReportNV> rep(64);
g.pTimings = rep.data();
dev.getLatencyTimingsNV(swapchain, &g);
// rep[i].simStartTime/renderSubmitEndTime/presentEndTime, gpuStart/EndUs…
VkLatencyTimingsFrameReportNV aşama-başına µs zaman damgaları
verir — simülasyon, render submit, present başlangıç/bitiş — NVIDIA
Reflex’in rapor ettiğiyle aynı veri; ‘T’de örneklenen girdi →
T+X ms’de fotonlar’ı tahmin yerine doğrulamanızı sağlar.
Hangisini kullanmalı
taşınabilir, minimal: present_wait — AMD/Intel/NV'de çalışır,
marker yok, döküm yok
derin teşhis + boost: low_latency2 — sadece-NVIDIA, ama
aşama-başına timing + saat boost + sleep alırsınızPratik kurallar
timeout = 0ilewaitForPresentbir sorgudur (“frame N sunuldu mu?”), gerçek timeout bir pace’tir — döngüde ikincisini kullanın.- Uçuşta frame’ler hâlâ önemli: pacing gecikmeyi ~1 refresh’te sınırlar — üstüne 4-derin kuyruk yığmayın.
lowLatencyBoostgüç yönetimiyle savaşır — pil/termal-duyarlı kullanımda açık bırakmayın.- Frame başına gerçek sunulma zaman damgasını almak için
VK_EXT_present_timingile eşleştirin —low_latency2‘ninpresentEndTime‘ı sürücünün tahmini, timing ekranın gerçeğini verir. - Wayland’de
wp_presentationgeri bildirimiyle birleştirin — her iki katmanda da uyu + doğrula + ölç.