Makale

Open GL öğreniyoruz

ÖNEMLİ NOT

Tüm bu seri boyunca okuyucunun C/C++ bildiği varsayılacak ve
teknik terimlerin açıklamaları düzenli bir şekilde verilecektir. Tüm kod
örnekleri
Courier fontu ile yazılacak ve aksi belirtilmedikçe C++ olacaktır.
Okuyucunun Visual C++ 6 (ya da üzeri / uyumlu bir C++ derleyicisi) kullandığı
varsayılacaktır.



Bölüm
1: Sistem Altyapısı




A-
SIMD mimarisi hakkında ön bilgi




Bu
yazı grafik programlama ile ilgili olmasına rağmen, ilk önce elimizdeki
sistemi tanıyarak ve bazı kurallar koyarak başlayacağız.



Bilgisayarlar
1983’den bu yana oldukça fazla gelişmelerine rağmen aynı taban üzerinde
ayakta kalmaktalar. Süreklilik gerektiren işlerde oldukça yetenekliler, fakat
düzensiz işlemlerde, örneğin değişken bir üç boyutlu sahneyi işlemekte,
oldukça yavaşlar. Oyunlarda sıkça yapılan şeylerden birisi bu şekildeki
verinin işlenmesi oldugu için, bu seride ilk yapacağımız şey,
bilgisayarların ne olduğunu (ve olmadığını) öğrenmek ve bu kısıtlamanın
üstesinden nasıl gelineceğini öğrenmek olacak.



Bir
günümüz bilgisayarı:

1- Doğrusal ve uzun bloklar halindeki işlemleri daha hızlı
yapabilir.

2- Ne kadar az hafıza erişimi yaparsa o kadar hızlı çalışır.

3- Elimizdeki “düzensiz” veri miktarı arttıkça
adresleme hataları da o denli artar.



Tüm
bunlar, Intel’in ve diğer firmaların şu anda kullandığı SIMD (Single
Instruction Multiple Data = Tek komutta birden çok veri) tarzı işletim ile
neredeyse tamamen örtüşen bir durum oluşturuyor. İlk kısıtlamamiz, bize
uzun blokların tek seferde daha hızlı işlendiğini söylüyor, bu da tam
olarak SIMD’nin yaptığı şey, uzun blokları tek seferde işlemek. Diğer iki
kısıtlama bize adres çözme işleminin problemlerini, yani “cache
miss” ve “misalign” sorunlarını anlatıyor. “Cache
miss” ile kastedilen, bir adres çözme işlemi sonucu erişilecek verinin
kaşe bellekte bulunmadığının anlaşılıp yeniden bir adres çözme işlemine
yol açması. “Misalign” ise çözülen adresin 8 (ya da şimdilerde
16 byte)’ın katlarına denk gelmemesi, örneğin hafızanın 3. byte’ında
bulunması sonucu gereksiz miktarda verinin birden çok kez okunmasına yol açmasına
verilen isim.



Şimdi
bu ufak tablodaki kısıtlamalardan kurtulmak için bu seri boyunca uyacağımız
kurallara bir göz atalım:



1- SIMD ve/veya SIMD2 işlemlerine ağırlık vermek ya da bunlara dönüşebilir
şekilde kod yazmak.

2- Veriyi daima “hizalanmış” hafıza adreslerinden başlatmak.

3- Veriyi daima 16 byte’ın katları olarak tutmaya çalışmak, mümkünse
{X,Y,Z} gibi sırasız veri akışını {X[],Y[],Z[]} haline getirmek.



Üçüncü
madde biraz karışık gelebilir. Tipik bir programda sakladığımız veri eğer



class nokta

{

     
float x,
y,
z;

};

nokta benimNoktalarım[1024];



ise,
bu SIMD için daha uygun olan:



class yogunNokta

{

     
float x[1024],
y[1024],
z[1024];

};

yogunNokta benimNoktalarim;

 

şeklinde
yeniden düzenlenmelidir. Tabii bu şekilde statik veri ayrılması pek önerilen
birşey olmadığı için bunu sadece konuyu açıklamak için verdiğimiz bir
örnek olarak yorumlamanız gerekiyor, pratikte:



class yogunNokta

{

     
float
*x,* y,
*
z;

};

yogunNokta benimNoktalarim;



x=(float*) aligned_malloc( 1024 * sizeof(float),
16);

y=(float*) aligned_malloc( 1024 * sizeof(float),
16);

z=(float*) aligned_malloc( 1024 * sizeof(float),
16);



daha
doğru bir yaklaşım. Buradaki
aligned_malloc
komutu 16 byte’ın katı olan bir adresten başlayacak şekilde bellek ayırmamızı
sağlıyor.



B- Optimizasyon



Kullandığımız sistem, herşeyden önce, saf C/C++
kodunun daima en basit hedef işlemciye göre (zorlanmadıkça) derlendiği bir
sistemdir. Yani yazdığımız aşağıdakine benzer bir satır, eğer biz
derleyiciyi buna zorlamazsak, 80386’da hızlı çalışacak şekilde derlenir:



float
fArray[16] = {1.0f, 4.6f, 12.0f, 43.0f, 61.0f, 52.0f, 2.0f, 1.0f, 3.0f, 4.0f,
6.0f, 13.0f, 421.0f, 54.0f, 654.0f, 1.0f};

for(unsigned long i=0;
i<16; i++)

{

      fArray[i] *= 2.49f;

}



Bunun
assembly karşılığı, standart derleyici çıktısı olarak:



xor  
eax, eax

$loop1:

fld  
DWORD PTR _fArray$[esp+eax*4+64]

fmul 
DWORD PTR __real@401f5c29

inc  
eax

cmp  
eax, 16

fstp 
DWORD PTR _fArray$[esp+eax*4+60]

jb   
SHORT $loop1



olacaktır.
Birinci kuralı ihlal ettik. Burada işlemcinin FPU kısmı kullanılarak aynı
anda sadece bir sayı ile çarpma yapılmaktadır. Bunun yerine eğer bir P3/P4
derleyicisi ve el ile optimizasyon yöntemleri kullanırsak yukarıdaki kod şu
şekli alır:



float fArray[16]
= {1.0f, 4.6f, 12.0f, 43.0f, 61.0f, 52.0f, 2.0f, 1.0f, 3.0f, 4.0f, 6.0f, 13.0f,
421.0f, 54.0f, 654.0f, 1.0f};

float fPack[4]
= {2.49f, 2.49f, 2.49f, 2.49f};

// 4 adet çarpan


float *p1,*p2;

p1 = &(fArray[0]);

p2 = &(fPack[0]);

_asm{

     
mov ecx,4

     
mov eax, p2

     
movups xmm1, [eax]

     
mov eax, p1

mLoop1:

     
movups xmm0, [eax]

     
mulps xmm0, xmm1

     
movups [eax], xmm0

     
add eax, 0x10 // 16
, 4 tane 4 byte(float)

     
loop mLoop1

};



Görüldüğü
gibi artık tek komutta aynı anda dört çarpma yaparak daha uzun fakat daha hızlı
bir kod elde ettik. Buradan da şu yeni kural çıkıyor:



Uzun kod daha hızlı çalışabilir.



Tabii
bu kullanılan işlemciye göre değişeceği için OpenGL ya da DirectX gibi kütüphanelerde
aynı rutin birden fazla işlemciye göre yeniden yazılmış şekillerde
bulunurlar.

 

Bölüm
2: OpenGL


 

A-
OpenGL mi DirectX mi?


 

Yeni
programlamaya başlayanlar için çok daha az zahmetli olacağına inandığım
OpenGL ile seriye başlamak istiyorum. DirectX her ne kadar daha “metale
yakın” bir tarza sahip olsa da, burada okuyucunun derleyici ile yüksek
bir ihtimalle beraber kurmuş olacağı OpenGL kütüphanelerini kullanacağız.
Bu arada çoğu kullanıcının bir çeşit nVidia ya da ATI görüntü kartına
sahip olduğunu ve en son OpenGL destekli (beta olmayan) sürücüleri yüklemiş
olduğunu da varsayıyoruz.



OpenGL,
DirectX’in aksine sürekli genişletilen (ve genişletilebilen) bir altyapıya
sahip. Örneğin eğer bir firma bir kartı bu gün piyasaya çıkartır ve
“raytrace destekliyor” olduğunu söylerse, bunun için (SGI ile konuşup
eklentiyi kayıt ettirmenin yanında) tek yapmaları gereken bir adet
“eklenti” yazmak ve bunu sürücülerine dahil etmek olacaktır.
DirectX bu şekilde genişletilemez, sadece “yazılım ile render”
eklentilerini kabul eder ve bunlar da donanınm ile çalışmazlar, sadece yazılımsal
eklerdir ve “yeterince hızlı” değillerdir.

 

OpenGL
desteğimiz tam olduğuna göre, ilk OpenGL testimiz ile işe başlayabiliriz…

 

Etiketler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu