Frame pacing ve vkWaitForPresentKHR ile Vulkan'da girdi-foton gecikmesi nasıl en aza indirilir

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

queue.txt
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 lag

VK_KHR_present_wait

wait.cpp
// 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

ll2.cpp
// 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ı

choose.txt
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ız

Pratik kurallar

  • timeout = 0 ile waitForPresent bir 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.
  • lowLatencyBoost güç 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_timing ile eşleştirin — low_latency2‘nin presentEndTime‘ı sürücünün tahmini, timing ekranın gerçeğini verir.
  • Wayland’de wp_presentation geri bildirimiyle birleştirin — her iki katmanda da uyu + doğrula + ölç.

Check out similar posts by category: Vulkan C++