Mobil GPU’lar DRAM’e render etmez — küçük bir on-chip tile buffer’ına (örn. 128×128 piksel) render eder, resolve eder, sonra sonraki tile’a geçer. Bir render target’ı sample etmek için geri okumak bir tile flush’a zorlar — tam olarak tile GPU’ların var olma nedeninin önlediği şey. Subpass’ler + input attachment’lar çözümdür: ikinci subpass, birinci subpass’in çıktısını hâlâ tile üzerindeyken okur, belleğe hiç dokunmaz.
Zihinsel model
tile.txt
subpass 0: albedo/normal/depth'i color attachment'lara render et
(tile belleğinde kalır — henüz resolve yok)
subpass 1: lighting pass — albedo/normal'i input_attachment ile okur
son rengi yazar
→ sadece BİR DRAM resolve'u, sondaSubpass’siz: pass 1 DRAM’e yazar, pass 2 geri okur — iki tam framebuffer gidiş-dönüşü. Subpass’li: sıfır.
Kurulum
subpass.cpp
// iki subpass, bir render pass:
vk::AttachmentReference gbufferRefs[] = {albedoRef, normalRef};
vk::SubpassDescription sp0{};
sp0.colorAttachmentCount = 2;
sp0.pColorAttachments = gbufferRefs;
sp0.pDepthStencilAttachment = &depthRef;
vk::AttachmentReference inputRefs[] = {albedoIn, normalIn, depthIn};
vk::SubpassDescription sp1{};
sp1.inputAttachmentCount = 3;
sp1.pInputAttachments = inputRefs; // subpass 0'ın çıktısını OKU
sp1.colorAttachmentCount = 1;
sp1.pColorAttachments = &finalRef;
// bağımlılık — subpass 1, subpass 0'ın yazmalarını bekler:
vk::SubpassDependency dep{};
dep.srcSubpass = 0;
dep.dstSubpass = 1;
dep.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
dep.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
dep.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader;
dep.dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead;Shader tarafı
light.frag
// input_attachment_index N, set/binding — AYNI pikseli okur:
layout(input_attachment_index = 0, set = 0, binding = 0)
subpassInput gAlbedo;
layout(input_attachment_index = 1, set = 0, binding = 1)
subpassInput gNormal;
void main() {
vec4 albedo = subpassLoad(gAlbedo); // UV yok — aynı-piksel okuması
vec3 n = subpassLoad(gNormal).xyz;
// aydınlatma…
}subpassLoad‘un koordinatı yoktur — geçerli fragment’in pikselini
okur. Onu tile-local yapan kısıt budur.
Hâlâ önemli olduğu yerler
- Mobil/tile GPU’lar (Mali, Adreno, PowerVR) — tüm amaç bu; DRAM gidiş-dönüşleriyle deferred rendering bir performans uçurumudur.
- Masaüstü immediate renderer’lar — orada subpass’ler etkisizdir (masaüstü okumaları zaten cache’ten geçer); dynamic rendering + ayrı pass’ler daha basittir.
- Deferred shading — klasik kullanım; forward+ daha az faydalanır.
Pratik kurallar
- gbuffer attachment’larında
loadOp = eClear/eDontCarevestoreOp = eDontCare(pass içinde tüketilir, resolve edilmez) — verinin tile ile öldüğünü sürücüye söylemek perf kazancıdır. - Yalnızca
subpassLoad—texture()sampling yok, offset’litexelFetchyok; input attachment’lar kesinlikle aynı-pikseldir. - gbuffer image’larında
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT+MEMORY LazilyAllocated— tile GPU’larda “bellek” sadece tile’dır; DRAM’de hiç allocate edilmeyebilir. VK_KHR_dynamic_rendering+vkCmdNextSubpass… dynamic rendering içinvkCmdNextSubpassyoktur — subpass’ler bir render-pass-object özelliğidir; dynamic rendering bunun yerineVK_KHR_dynamic_rendering_local_readkullanır (aynı fikir, farklı tesisat).- Bir input attachment = bir piksel okuması; bir komşuluk örneklemek için hâlâ gerçek texture + ayrı pass gerekir — subpass’ler blur yapamaz.
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow