Advanced Data (İleri Seviye Veriler)

Çoğu bölümde GPU üzerinde veri depolamak için OpenGL'de buffer'ları yoğun biçimde kullandık. Bu bölümde buffer'ları yönetmenin birkaç alternatif yaklaşımını kısaca ele alacağız.

OpenGL'de bir buffer, özünde belirli bir GPU bellek parçasını yöneten bir nesneden başka bir şey değildir. Buffer'a bağlandığı buffer target'a göre anlam kazandırırız. Bir buffer yalnızca GL_ARRAY_BUFFER'a bağlandığında vertex array buffer olur; aynı buffer'ı GL_ELEMENT_ARRAY_BUFFER'a da rahatlıkla bağlayabiliriz. OpenGL her target için buffer'a bir referans tutar ve target'a göre buffer'ı farklı biçimde işler.

Şimdiye kadar buffer belleğini glBufferData çağrısıyla doldurduk; bu fonksiyon bir GPU bellek parçası tahsis eder ve bu belleğe veri ekler. Veri argümanı olarak NULL geçirilirse fonksiyon yalnızca bellek tahsis eder, doldurmaz. Bu, önce belirli bir miktarda belleği rezerve etmek, daha sonra geri gelip buffer'ı doldurmak istediğimizde kullanışlıdır.

Tüm buffer'ı tek seferde doldurmak yerine, glBufferSubData çağrısıyla buffer'ın belirli bölgelerini de doldurabilirsiniz. Bu fonksiyon buffer target, offset, verinin boyutu ve verinin kendisini argüman olarak alır. glBufferData'dan farkı, buffer'ı nereden doldurmaya başlayacağımızı belirleyen bir offset belirtebilmesidir. Bu sayede buffer belleğinin yalnızca belirli kısımlarını ekleyebilir veya güncelleyebiliriz. Buffer'ın yeterli tahsis edilmiş belleğe sahip olması gerektiğinden, glBufferSubData çağrısından önce mutlaka glBufferData çağrısı yapılmalıdır:

glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // Aralık: [24, 24 + sizeof(data)]

Veriye buffer'a aktarmanın bir diğer yolu da buffer belleğine bir işaretçi istemek ve veriyi doğrudan bellekte kendiniz kopyalamaktır. glMapBuffer çağrısı ile OpenGL, işlem yapmamız için bağlı buffer'ın belleğine bir işaretçi döndürür:

float data[] = {
  0.5f, 1.0f, -0.35f
  [...]
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// işaretçiyi al
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// veriyi belleğe kopyala
memcpy(ptr, data, sizeof(data));
// işaretçiyle işimizin bittiğini OpenGL'e bildir
glUnmapBuffer(GL_ARRAY_BUFFER);

glUnmapBuffer ile işaretçi işlemlerini tamamladığımızı OpenGL'e bildirdiğimizde işaretçi geçersiz hâle gelir; fonksiyon, verinin buffer'a başarıyla eşlenip eşlenmediğini GL_TRUE olarak döndürür.

glMapBuffer, veriyi geçici bir belleğe depolamadan doğrudan buffer'a eşlemek için kullanışlıdır; örneğin bir dosyadan veri okuyup doğrudan buffer belleğine kopyalamak gibi durumlarda işe yarar.


Vertex Attribute'larını Toplu Hâlde Gruplamak

glVertexAttribPointer ile vertex array buffer içeriğinin attribute düzenini belirtebiliyorduk. Vertex array buffer içinde attribute'ları interleaved (iç içe) biçimde tutuyorduk; yani her vertex için konum, normal ve/veya doku koordinatlarını bellekte yan yana yerleştiriyorduk. Artık buffer'lar hakkında biraz daha bilgi sahibi olduğumuza göre farklı bir yaklaşım deneyebiliriz.

Tüm vektör verilerini interleaved yerleştirmek yerine, her attribute türüne göre büyük bölümler hâlinde toplayabiliriz. 123123123123 gibi interleaved bir düzen yerine 111122223333 biçiminde toplu bir düzen kullanabiliriz.

Dosyadan vertex verisi yüklerken genellikle bir konum dizisi, bir normal dizisi ve/veya bir doku koordinatı dizisi elde edersiniz. Bu dizileri bir araya getirerek interleaved büyük bir dizi oluşturmak biraz çaba gerektirebilir. Toplu yaklaşım, glBufferSubData ile kolayca uygulayabileceğimiz daha pratik bir çözümdür:

Bu sayede attribute dizilerini önce işlemeden doğrudan buffer'a aktarabilirsiniz. Bunları tek büyük bir dizide birleştirip glBufferData ile doldurmak da mümkündür; ancak glBufferSubData bu tür görevlere özellikle uygundur.

Bu değişiklikleri yansıtmak için vertex attribute pointer'larını da güncellememiz gerekir:

stride parametresinin vertex attribute boyutuna eşit olduğuna dikkat edin; bir sonraki vertex attribute vektörü, 3 (veya 2) bileşeninin hemen ardından bulunur.

Bu yaklaşım da vertex attribute'larını ayarlamak ve belirtmek için kullanılabilecek geçerli bir yöntemdir; daha düzenli bir yapı sunar. Bununla birlikte, her vertex shader çalışmasında vertex attribute'larının bellekte yakın hizalanması nedeniyle interleaved yaklaşım hâlâ önerilen yöntemdir.


Buffer'ları Kopyalamak

Buffer'larınıza veri doldurduktan sonra bu veriyi diğer buffer'larla paylaşmak ya da bir buffer'ın içeriğini başka birine kopyalamak isteyebilirsiniz. glCopyBufferSubData fonksiyonu, bir buffer'daki veriyi diğerine kolaylıkla kopyalamamızı sağlar. Fonksiyon prototipi şu şekildedir:

readtarget ve writetarget parametreleri kopyalamak istediğimiz buffer target'larını alır. Örneğin VERTEX_ARRAY_BUFFER'dan VERTEX_ELEMENT_ARRAY_BUFFER'a kopyalamak için bu target'ları okuma ve yazma hedefi olarak belirleyebilirsiniz; söz konusu target'lara bağlı buffer'lar etkilenecektir.

Peki ya aynı anda iki farklı vertex array buffer'ına okuma ve yazma yapmak istersek? İki buffer'ı aynı anda aynı target'a bağlayamayız. Bu nedenle OpenGL, GL_COPY_READ_BUFFER ve GL_COPY_WRITE_BUFFER adında iki yeni buffer target daha sunar. İstediğimiz buffer'ları bu yeni target'lara bağlarız ve bunları readtarget ile writetarget argümanları olarak geçiririz.

glCopyBufferSubData, verilen readoffset'ten başlayarak belirtilen size kadar veriyi okur ve writetarget buffer'ına writeoffset konumundan yazır. İki vertex array buffer'ının içeriğini kopyalamaya ilişkin bir örnek:

Aynı işlemi yalnızca yazma hedefi buffer'ını yeni target türlerinden birine bağlayarak da yapabiliriz:


Buffer'ları nasıl kullanacağınız hakkında biraz daha bilgi sahibi olduğunuzda, onları çok daha ilginç biçimlerde kullanmaya başlayabilirsiniz. OpenGL'de ilerledikçe bu yeni buffer yöntemleri giderek daha kullanışlı hâle gelir. Bir sonrakiarrow-up-right bölümde uniform buffer nesnelerini ele alırken glBufferSubData'yı yoğun biçimde kullanacağız.

Last updated