[ silvervanja @ 17.10.2005. 13:01 ] @

kljucna rec: GL_EXT_pixel_buffer_object

jel' ima neko primer za ovu ekstenziju? ja sam nasao nesto i radi mi na 900MBps u jednom smeru, ili 280MBps prenetih u celom krugu (iz sistemske memorije u PBO pusher, iz PBO pusher-a na karticu, sa kartice u PBO puller, iz PBO puller-a u sistemsku memoriju). skoro bez opterecenja... jel' ima neko bolje?
[ yooyo @ 17.10.2005. 13:21 ] @
Ovde (http://developer.nvidia.com/object/fast_texture_transfers.html) imas objasnjenje za PBO. Ja sam na GF 5900 i na 6800 uspeo da posaljem ~1.8 GB/sec. Readback dosta zavisi od ploce i procesora. 6800 GT i 6800 Ultra na APG8x ploci imaju 450-550MB/sec u zavisnosti od procesora (3Ghz = 450MB/sec, 3.2Ghz = 550MB/sec). 5x00 serija je duplo sporija, a 7800 serija je iste brzine kao i 6x00.

U svakom slucaju, BGRA format je akcelerisan dok RGB nije.

Fora kod readbacka je da napravis 2 PBO-a i da i prvi posaljes gornji deo ekrana a u drugi donji deo. Na ovaj nacin moguce je paralelno kopirati podatke.

Jos nesto.. ako koristis Lock/memcpy/Unlock mehanizam, da prebacis podatke u/iz systemske memorije potrudi se da nadjes dobru memcpy rutinu (po mogucstvu da korisi MMX/SSE registre). Sa dobrom i brzom memcpy rutinicom mozes da dobijes ~100MB/sec

Mozes da koristis i glGetBufferData ali on izgleda koristi standardnu memcpy rutinu pa nije sampion brzine. Kukao sam NVidii da ovo poprave u driveru, pa kad mi jave, obavesticu te.

btw.. Za sta pokusavas da iskoristis PBO ako nije tajna?

yooyo

Edit: komentar o memcpy i glGetBufferData

[Ovu poruku je menjao yooyo dana 17.10.2005. u 14:25 GMT+1]
[ silvervanja @ 17.10.2005. 15:42 ] @
1.8GBps, to je bas puno... moja verzija radi na 0.9GBps, kao sto rekoh.

sto se memcpy tice, napisao sam mali SSE2 kod koji cita po 256 bajta u petlji, to kod mene (intel Pentium IV 3.2) radi nesto brze od 1.8GBps (windows memcpy radi na 0.85GBps). na AMD-u radi sporije, oni izgleda nemaju movdqa i movntdq... ali imam brze verzije za AMD (do 2.2GBps).

hvala za link na pdf, moracu da gra proucim, ...

sto se svrhe tice, PBO mi treba za streaming podataka na GPU gde ih obradjujem i vracam nazad na CPU. GPU koristim kao target DSP karticu;)

sta ti radis?

ako hoces da se menjamo;) SSE2 za PBO kod?
[ yooyo @ 17.10.2005. 21:28 ] @
Uploading textura:

Code:

int texsize = xres*yres*4;

// kreiranje PBO buffera
glGenBuffers(MAX_PBO, &PBOBuffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, PBOBuffe);
glBufferData(GL_PIXEL_UNPACK_BUFFER_EXT, texsize, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);



// kreiranje texture
glGenTextures(1, &texture);
glBindTexture(texTarget, texture); // texTarget moze biti GL_TEXTURE_2D, GL_TEXTURE_RECTANLE_EXT/ARB/NV
glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(texTarget, GL_TEXTURE_WRAP_S, GL_CLAMP); // stavio sam clamp, ali moze i repeat
glTexParameteri(texTarget, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(texTarget, 0, GL_RGBA, xres, yres, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); 
glBindTexture(texTarget, 0); 



// Uploading texture
glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, PBOBuffer);

// Prvi nacin: za sada sporiji, ali ako je driver optimizovan onda moze biti brzi
glBufferSubData(GL_PIXEL_UNPACK_BUFFER_EXT, 0, texsize, pointer);

// Drugi nacin: brzi ako se koristi specijalizovana memcopy rutina
void *Memory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, GL_WRITE_ONLY);
MyMemCopy(Memory, pointer, texsize); 
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_EXT);

//Treci nacin: ako CPU generise texturu tako sto sekvencijalno popunjava memoriju
//onda moze da koristis drugi nacin samo sto umesto MyMemCopy ubaci rutinu koja
//generise podatke za texturu

glBindTexture(texTarget, texture);

glTexSubImage2D(texTarget, 0, 0, 0, xres, yres, GL_BGRA, GL_UNSIGNED_BYTE, 0);
ili
// ovo bi trebalo da je brze
glTexSubImage2D(texTarget, 0, 0, 0, xres, yres, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);

glBindTexture(texTarget, 0);


glBindBuffer(GL_PIXEL_UNPACK_BUFFER_EXT, 0);


Readback:
Code:

// koristi se vise PBO buffera da bi se postigao paralelni transfer

// inicijalizacija:
#define MAX_RBPBO 2
GLuint PBOreadback[MAX_RBPBO];

int FrameSize = ViewPortSize.x * ViewPortSize.y * 4;
int FramePartSize = FrameSize / MAX_RBPBO;

// Alokacija sistemske memorije za kopiranje backbuffera
Data = (GLubyte*) malloc(m_Data, m_FrameSize);

// Inicijalizacija PBOreadback buffera
glGenBuffers(MAX_RBPBO, PBOreadback);
for (int i=0; i<MAX_RBPBO; i++)
{
  glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBOreadback[i]);
  glBufferData(GL_PIXEL_PACK_BUFFER_EXT, FrameSize, NULL, GL_STATIC_READ);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER_EXT, 0);



// Readback code
// Zapocinje transfer iz backbuffera u PBO. 
// Vodi racuna da je visina backbufera deljiva sa MAX_RBPBO, 
// da ne bi doslo do greske u kopiranju
for (i=0; i<MAX_RBPBO; i++)
{
 glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBOreadback[i]);
 glReadPixels(0, i*ViewPortSize.y/MAX_RBPBO, 
              ViewPortSize.x, ViewPortSize.y / MAX_RBPBO,  
              GL_BGRA, 
              /*GL_UNSIGNED_BYTE ili*/ GL_UNSIGNED_INT_8_8_8_8_REV,
              BUFFER_OFFSET(0));
}

// Kopiranje iz PBO-a u sistemsku memoriju
int i;
for (i=0; i<MAX_RBPBO; i++)
{
 glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, PBOreadback[i]);

// prvi nacin: koriscenjem MyMemCopy
 void* pboMemory = glMapBuffer(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY);
 MyMemCopy(FramebufferData + i*FramePartSize, pboMemory, FramePartSize);
 glUnmapBuffer(GL_PIXEL_PACK_BUFFER_ARB);

 // Drugi nacin: koriscenjem glGetBufferSubData
 glGetBufferSubData(GL_PIXEL_PACK_BUFFER_ARB, 0, 
               FramePartSize, FramebufferData + i*FramePartSize);
}


Jos par sitnica... Obzirom na to da se na ovaj nacin postize asinhroni transfer podataka izmedju CPU i GPU potrebno je da modifikujes program da maksimalno paralelizujes radnje. Npr... Pre pocetka renderinga pokreni uploading texture za sledeci frame (ako mozes) ili pokreni readback na kraju frejma ali podatke iz PBO prebaci u sistemsku memoriju na pocetku sledeceg frejma. Ukratko.. ako se neki od objekata nad koji se upravo vrsi asinhroni transfer koristi u renderingu onda ce se GPU zaustaviti dok objekat ne bude spreman.

yooyo
[ silvervanja @ 18.10.2005. 09:50 ] @
void memcpy08(void *dst, const void *src, size_t count)
{
_asm {
mov esi, src
mov edi, dst
mov ecx, count
shr ecx, 8 // 256 bytes per iteration

prefetchnta 0[ESI] // Prefetch next loop, non-temporal
prefetchnta 128[ESI]
prefetchnta 256[ESI]
prefetchnta 384[ESI]

loop1:
prefetchnta 512[ESI] // Prefetch next loop, non-temporal
prefetchnta 640[ESI]

movdqa xmm0, 0[ESI] // Read in source data
movdqa xmm1, 16[ESI]
movdqa xmm2, 32[ESI]
movdqa xmm3, 48[ESI]
movntdq 0[EDI], xmm0 // Non-temporal stores
movntdq 16[EDI], xmm1
movntdq 32[EDI], xmm2
movntdq 48[EDI], xmm3

movdqa xmm4, 64[ESI]
movdqa xmm5, 80[ESI]
movdqa xmm6, 96[ESI]
movdqa xmm7, 112[ESI]
movntdq 64[EDI], xmm4
movntdq 80[EDI], xmm5
movntdq 96[EDI], xmm6
movntdq 112[EDI], xmm7

movdqa xmm0, 128[ESI] // Read in source data
movdqa xmm1, 144[ESI]
movdqa xmm2, 160[ESI]
movdqa xmm3, 176[ESI]
movntdq 128[EDI], xmm0 // Non-temporal stores
movntdq 144[EDI], xmm1
movntdq 160[EDI], xmm2
movntdq 176[EDI], xmm3

movdqa xmm4, 192[ESI]
movdqa xmm5, 208[ESI]
movdqa xmm6, 224[ESI]
movdqa xmm7, 240[ESI]
movntdq 192[EDI], xmm4
movntdq 208[EDI], xmm5
movntdq 224[EDI], xmm6
movntdq 240[EDI], xmm7

add esi, 256
add edi, 256
dec ecx
jnz loop1

emms
}
}

evo obećanog memcpy koda... kod mene radi na 1.8GBps (PIV 3.2GHz FSB800). javi na koliko radi kod tebe.
nisam stigao da pogledam PBO kod, ali ću to uraditi kasnije danas, pa se javljam.
[ yooyo @ 18.10.2005. 11:35 ] @
Brza ti je rutinica... Ja imam joednu koju sam nasao na AMD sajtu. Pogledaj je mozda ti se svidi. Radi mozda malo brze od tvoje (1-2%) i to na Intelu P4 3.2GHz HT. Nisam je probao na AMD-u jer nemam nijednu AMD masinu u blizini :)

Inace, ja sam koristio PBO u programu koji sam pravio za TV Metropolis i sluzi za emitovanje TV programa. Cela TV Metropolis ide sa kompjutera i korisi OpenGL za rendering videa i ostalih grafickih layera.

yooyo

[Ovu poruku je menjao yooyo dana 18.10.2005. u 12:35 GMT+1]
[ silvervanja @ 28.10.2005. 20:48 ] @
pogledao sam tvoj kod, zapravo se veoma malo razlikuje od mog. ti svaki frejm deliš na dva pa obrađuješ prvu polovinu dok druga stiže. kod mene je to algoritamski nemoguće (treba mi ceo frejm da bi nešto radio sa njim), tako da ja pajplajnujem koristeći kružne bafere (po dva za push i pull u sistemskoj memoriji, i po dva za u push i pull pbo baferima - ukupno osam). ali imam samo jednu teksturu koju koristim za destination iz push pbo bafera (ne znam da li bi se nešto dobilo ako i ovde uvedem kružne bafere?).

znači, dok se trenutni strimuje sa gpu u pull pbo, ja strimujem iz prethodnog pull pbo u pull u sistemskom memoriji. slično i za push stranu. na ovaj način sam dobio da se pbo uglavnom odvija u pozadini. 80% cpu vremena mi ide na memcpy, 5% na glBegin, glEnd, 15% je čekanje na glReadPixels. ovih 15% je problem i verovatno se može svesti na <1% kao kod glTexSubImage2D() dela.

zašto sam sve ovo pisao? pa, nema još mnogo mesta za optimizacije (15% max), ali sam još uvek jako daleko od gigabajtnog prenosa. u petlji uspevam da prenesem 480MB u sekundi (gde je petlja: push sistemska memorija -> push pbo buffer -> gpu -> pull pbo buffer -> pull sistemska memorija). takođe, standalone download (bez readback-a, samo transfer iz push pbo bafera -> gpu) radi na 900MBps. kako si isterao 1.8GB? ako nije tajna...

test platforma mi je NV6800, PIV 3.2GHz, FSB800.