holodepth

Three.js · İleri geometri

Interleaved Buffers: Veri Paketleme Sanatı ve GPU Bant Genişliği

Stride, offset ve bellek yerelliği ile CPU–GPU köprüsünü verimli kullanmak

Modern grafik programlamada hız, sadece ne kadar veri gönderdiğinizle değil, o veriyi GPU belleğine nasıl yerleştirdiğinizle ilgilidir. Advanced Geometri serisinin bu bölümünde, veriyi “dağınık” tutmak yerine içe içe (interleaved) dizmenin, işlemci ve grafik kartı arasındaki dar köprüde trafik akışını nasıl iyileştirebileceğini inceliyoruz.

Ön bilgi için Attributes & buffer’lar; indeks düzeni için Indexed vs non-indexed; çoklu kopya için Instancing.

Bellek mimarisi: sıralı (sequential) ve içe içe (interleaved)

Normal bir BufferGeometry yapısında veriler çoğu zaman ayrı attribute dizilerinde tutulur: pozisyonlar bir Float32BufferAttribute, renkler başka bir buffer’da, UV koordinatları yine ayrı bir blokta. Bu düzen okunması kolay ve araç zinciriyle uyumludur; fakat GPU bir vertex işlerken birden fazla bellek bölgesine sırayla dokunmak zorunda kalabilir.

Non-interleaved (sıralı) yapı

Bu yapıda GPU, tek bir köşe için veriyi toplarken bellekte atlama yapabilir:

  • “Pozisyon listesinin 0. elemanını getir.” (ör. bellek bölgesi A)
  • “UV listesinin 0. elemanını getir.” (ör. bellek bölgesi B)
  • “Normal listesinin 0. elemanını getir.” (ör. bellek bölgesi C)

Bu tür atlama, cache miss (önbellek ıskalaması) riskini artırır; çekirdek veriyi beklerken pipeline verimli dolmayabilir. Özellikle yoğun vertex iş yükünde maliyet hissedilir.

Interleaved (içe içe) yapı

Interleaved düzende, bir köşeye ait kanallar bellekte bitişik bir paket olarak durur — tipik olarak pozisyon, UV, normal vb. aynı “stride” bloğu içinde sırayla dizilir. Kavramsal örnek (her köşe için kanal sırası sabit varsayılmıştır):

[ Px, Py, Pz, U, V, Nx, Ny, Nz,  Px, Py, Pz, U, V, Nx, Ny, Nz, … ]

GPU bir köşe okumasına başladığında, o köşeye ait alanlar aynı bellek yakınlığında bulunur. Buna memory locality (bellek yerelliği) denir; önbellek hatları için elverişlidir.

Teknik kavramlar: stride ve offset

Interleaved buffer kullanırken GPU’ya “paketin şeklini” anlatmanız gerekir. İki kritik terim:

  • Stride (adım): Bir köşe kaydının toplam byte veya float cinsinden uzunluğu. Örneğin pozisyon (3 float) + UV (2 float) kullanıyorsanız, bir sonraki köşeye geçmek için paket başına 5 float atlanır — stride burada 5 float’tır (byte cinsinden: 5 × 4 = 20 byte, yalnızca bu iki kanal için).
  • Offset (sapma): Aynı paket içinde belirli bir attribute’un nereden başladığı. Pozisyonlar paketin başındaysa offset 0; UV’ler hemen ardından geliyorsa offset 3 (üç float atlayarak) gibi düşünülür.

Aşağıdaki canlı demoda örnek paket pozisyon (3) + UV (2) + normal (3) olup stride 8 float (32 byte) seçilmiştir; yalnızca pozisyon + UV düşünüldüğünde çıkan 5 float örneği ayrı bir sadeleştirmedir.

Three.js tarafında bu mantık InterleavedBuffer ile tek ham TypedArray üzerinde kurulur; her kanal için InterleavedBufferAttribute ilgili itemSize, offset ve paylaşılan stride ile tanımlanır.

Canlı demo: Sık düzlem ızgarası, sıralı vs interleaved

Aşağıdaki model yoğun bir PlaneGeometry ızgarasıdır (20×20 segment → 441 köşe). Geometri aynı kalır; yalnızca GPU’ya giden buffer düzeni değişir. Şematik şerit, sıralı modda pozisyon / UV / normal bloklarının ayrı ayrı dizildiğini; interleaved modda ise her köşe için 8 float’lık paket (3+2+3) tekrarını gösterir. Ok, sıralı modda bloklar arasında zıplayarak, interleaved modda ise düz süzülerek gider; vertex noktalarındaki hafif titreşim / nabız da tamamen öğretici bir vurgudur (ölçüm değildir). HUD’daki 3 fetch → 1 fetch ifadesi GPU mental modelini hatırlatır.
Aşağıdaki demo sabitleri, doc-interleaved-demo.js içindeki initInterleavedGridDemo ve buildPlaneGeometries ile aynı rakamları özetler.

Plane grid · buffer düzeni
Köşe Stride 8 float (32 byte) Offset P/U/N 0 / 3 / 5 3 fetch 1 fetch
Pozisyon (3) UV (2) Normal (3)

Bellek şeridi (şematik · veri akışı)

Altın cümle

Interleaved, GPU’nun veriyi tek seferde okumasını sağlar — performans buradan gelir.

Özet

3B sahnede görünen ızgara aynıdır; fark BufferGeometry attribute’larının arka planda ayrı mı yoksa InterleavedBuffer üzerinde paketlenmiş mi sunulduğundadır. Yoğun köşe sayısında bu ayrım, okunabilirlik ile bant genişliği / önbellek davranışı arasındaki klasik takastır.

Demo sabitleri tablosu (Plane grid · buffer)

SEG = 20 ile düzlem (20+1)² = 441 köşe üretir; sıralı ve interleaved geometriler aynı indeks ve aynı sayıda köşe paylaşır, yalnızca attribute düzeni değişir.

buildPlaneGeometries · initInterleavedGridDemo
Sahne / rol Parametre Değer Tür
Izgara SEG 20 → köşe 21×21 = 441 🔒 Sabit
Temel düzlem PlaneGeometry genişlik / yükseklik 2.35 × 2.35 · segment SEG × SEG · computeVertexNormals() 🔒 Sabit
Paket STRIDE_FLOATS / bayt 8 float (3+2+3) · 32 byte 🔒 Sabit
Interleaved attribute InterleavedBufferAttribute position · item 3 · offset 0 · normalized: false · uv · 2 · 3 · false · normal · 3 · 5 · true 🔒 Sabit
Sıralı kopya BufferGeometry position / uv / normal ayrı BufferAttribute · indeks base.index klonu 🔒 Sabit
Işık HemisphereLight 0xa7d8ff / 0x151820 · 1.15 🔒 Sabit
Işık AmbientLight / DirectionalLight beyaz ortam 0.38 · yönlü 2.35 · konum (3.5, 5.5, 4) 🔒 Sabit
Kamera PerspectiveCamera FOV 40 · yakın/uzak 0.08 / 80 · konum (0, 2.15, 3.35) · lookAt(0,0,0) 🔒 Sabit
Pivot Group.rotation başlangıç x = -π/2 + 0.28 · z = 0.12 · her kare y += dt × 0.22 🔒 Sabit
Yüzey MeshStandardMaterial 0x4a8fb8 · metalness 0.12 · roughness 0.42 · DoubleSide · flatShading: false 🔒 Sabit
Tel kafes LineBasicMaterial 0x6ec8ff · opaklık 0.35 🔒 Sabit
Vertex noktaları PointsMaterial taban boy 0.045 · opaklık 0.82 · sizeAttenuation: true · moda göre kare içi sinüs ile boy/opaklık animasyonu 🔒 + ↔ UI
Renderer makeRenderer ACESFilmicToneMapping · exposure 1.42 · SRGBColorSpace · setPixelRatio(min(dpr,2)) · setSize(…, true) 🔒 Sabit
Kök özniteliği data-ilv-layout sequential / interleaved (mod değişince güncellenir) ↔ JS
UI varsayılanları segment / onay Sequential · tel kafes açık · vertex noktaları açık ↔ HTML

Önemli kod kesiti

Sıralı geometri ayrı buffer’lardan; interleaved geometri tek Float32Array + InterleavedBuffer üzerinden kurulur.

const SEG = 20;
const STRIDE_FLOATS = 8;
const base = new THREE.PlaneGeometry(2.35, 2.35, SEG, SEG);
base.computeVertexNormals();
// seq: ayrı BufferAttribute klonları + index — dosyanın üstünde; burada interleaved paket:

const pos = base.attributes.position;
const uv = base.attributes.uv;
const nrm = base.attributes.normal;
const n = pos.count;
const arr = new Float32Array(n * STRIDE_FLOATS);
for (let i = 0; i < n; i++) {
  let o = i * STRIDE_FLOATS;
  arr[o++] = pos.getX(i);
  arr[o++] = pos.getY(i);
  arr[o++] = pos.getZ(i);
  arr[o++] = uv.getX(i);
  arr[o++] = uv.getY(i);
  arr[o++] = nrm.getX(i);
  arr[o++] = nrm.getY(i);
  arr[o++] = nrm.getZ(i);
}
const ib = new THREE.InterleavedBuffer(arr, STRIDE_FLOATS);
const ilv = new THREE.BufferGeometry();
if (base.index) ilv.setIndex(base.index.clone());
ilv.setAttribute("position", new THREE.InterleavedBufferAttribute(ib, 3, 0, false));
ilv.setAttribute("uv", new THREE.InterleavedBufferAttribute(ib, 2, 3, false));
ilv.setAttribute("normal", new THREE.InterleavedBufferAttribute(ib, 3, 5, true));

function applyMode() {
  const g = sequentialMode ? GEO_SEQ : GEO_ILV;
  mesh.geometry = g;
  points.geometry = g;
  wire.geometry = new THREE.WireframeGeometry(g);
}

Neden interleaved kullanılır?

Holodepth gibi yüksek performans hedefleyen uygulamalarda — özellikle binlerce benzer örneğin veya büyük parçacık benzeri veri akışlarının olduğu sahnelerde — paketleme fark yaratabilir:

  • CPU–GPU transferi: Tek büyük sürekli blok halinde veri göndermek, çok sayıda küçük parçaya bölmekten genelde daha verimlidir; sürücü ve API katmanında daha az fragmentasyon hedeflenir.
  • Önbellek dostu erişim: Bitişik düzen, L1/L2 önbellek davranışıyla daha uyumludur; vertex shader bir köşeyi işlerken sıradaki köşeye geçiş daha “düz” olabilir.
  • Güncelleme maliyeti: Pozisyon ve renk (veya başka kanallar) her karede birlikte değişiyorsa, tek buffer üzerinde güncelleme yapmak birden fazla buffer’ı senkron tutmaya göre daha basit ve hataya daha az açık olabilir.

HoloDepth uygulama notları

Three.js’te InterleavedBuffer ve InterleavedBufferAttribute ile bu yapı kurulur. Pratikte sık görülen kullanım: özel shader (RawShaderMaterial veya özel ShaderMaterial) ile attribute düzenini birebir kontrol ettiğiniz hatlar, ya da Lidar nokta bulutu, yoğun CAD verisi gibi büyük ve homojen vertex akışları.

Görsel / mimari ipucu: Interleaved düzen, instancing ile birleştiğinde tarayıcı tabanlı deneyimlerde “masaüstü oyun” hissi veren sahnelerde sık kullanılan bir optimizasyon katmanıdır — tek başına sihir değildir; draw çağrısı, materyal ve sahne düzeniyle birlikte düşünülmelidir.

Özet: ne zaman kullanmalı?

Bağlam Öneri
Standart uygulama, hızlı prototip, okunabilirlik öncelikli kod Çoğu zaman klasik BufferGeometry + ayrı attribute’lar yeterli ve bakımı kolaydır.
HoloDepth “pro” hattı: bant genişliği ve vertex güncellemesi kritik Profil çıkarın; darboğaz vertex ise interleaved + (gerekiyorsa) instancing / özel shader ile paketleyin.