Example: VulkanExamples/p44_occlusion
Serideki post 44 (post 11: depth attachment, post 30: timestamp query’leri).
Bir occlusion query GPU’da “bu draw’un kaç fragment’i depth test’ten
sağ çıktı?” sorusunu yanıtlar — görünürlük culling’in, lens-flare
fade’lerinin ve “bu kamera tile’ı ekranda mı” sorunsalının arkasındaki
primitive. vkCmdBeginQuery/EndQuery bir draw’u kuşatır;
vkGetQueryPoolResults query başına sample sayısı döndürür.
Kurulum
vk::QueryPoolCreateInfo qpi{};
qpi.setQueryType(vk::QueryType::eOcclusion).setQueryCount(2);
auto qpool = vk.device->createQueryPoolUnique(qpi);Bir pool N bağımsız query slot’u tutar — frame başına izlemek istediğiniz her nesne/hücre için bir tane.
Command buffer’da
c.resetQueryPool(qpool.get(), 0, 2); // cmd içinde reset ZORUNLU
quad(0, 0, 0.6f, 0.2f); // engelleyici (yakın, depth yazar)
c.beginQuery(qpool.get(), 0, {});
quad(0, 0, 0.6f, 0.8f); // aynı alan, arkada -> 0
c.endQuery(qpool.get(), 0);
c.beginQuery(qpool.get(), 1, {});
quad(-0.7f, -0.7f, 0.2f, 0.1f); // köşe, görünür -> >0
c.endQuery(qpool.get(), 1);Command buffer içindeki resetQueryPool zorunludur — query sonuçları
frame’ler arasında örtük olarak temizlenmez.
Readback
uint64_t res[2];
vk.device->getQueryPoolResults(
qpool.get(), 0, 2, sizeof(res), res, sizeof(uint64_t),
vk::QueryResultFlagBits::e64);query[0] occluded quad: 0 samples passed
query[1] visible quad : 1521 samples passed
Occlude edilmiş quad sıfır fragment geçirdi — sayım kesin: küçük 0.2 ölçekli quad 39×39 = 1521 piksel kaplar ve hepsi geçti, yani 1521.
Kontrol
# SPDX-License-Identifier: CC0-1.0
import re, subprocess
o = subprocess.run(["./app", "--headless", "out.ppm"], check=True,
capture_output=True, text=True).stdout
m0 = re.search(r"query\[0\] occluded quad: (\d+) samples", o)
m1 = re.search(r"query\[1\] visible quad : (\d+) samples", o)
assert m0 and m1, o
assert int(m0.group(1)) == 0, o # tamamen occlude -> sıfır
assert int(m1.group(1)) > 1000, o # görünür quad ~1521
print(f"OK: occlusion counts 0 / {m1.group(1)}")Nasıl KULLANILMAMALI
- Query bitmemiş olabilirse
eWithAvailabilitykontrolü olmadan sonuçları okumayın — draw’un bittiğini bilmiyorsanız (fence) veya availability flag’leri istemiyorsanızgetQueryPoolResultseNotReadydöndürür; bu demooneTimeile submit + bekler, yani sonuç hazırdır. - Niyet ettiğinizden fazlasını kuşatmayın —
begin/endarasında çizilen her şey sayılır, MSAA-resolve edilmiş fragment’ler dahil (sample sayılarıeSampleCount4vb. ile büyür; mutlak fragment sayıları gerekiyorsa aşama başına sayaçlar içinePipelineStatisticsquery’leri kullanın). - Aynı frame’de nesne başına query’lerde spin yapmayın — GPU query’leri güzel pipeline’lar ama aynı frame’de geri okumak stall’lar; üretim kodu pool’u double-buffer’lar ve sonuçları bir frame geç okur (readback ring ile aynı fikir).
eOcclusionvsVK_QUERY_CONTROL_PRECISE_BIT—ePreciseolmadan sayım bir üst sınır olabilir (bazı donanımlar early-z öncesi geçişleri sayar); “hiç fragment var mı” için precise modu kullanın.
Bu serideki tüm kaynak kod CC0 1.0 Universal (CC0-1.0) altında yayımlanmıştır — kamu malı tahsisi, atıf gerekmez.