• Non ci sono risultati.

Condizioni al contorno

4.6 Una nuova strategia di calcolo

4.6.4 Condizioni al contorno

In precedenza si `e pi`u volte preannunciato che il calcolo general purpose in GPU possa presentare grandi difficolt`a realizzative. E curioso notare co-` me tali problematiche si annidino molto pi`u frequentemente nella scrittura dell’applicazione principale che piuttosto nella definizione degli shader ve- ri e propri. E, comunque, da ricordare che, non esistendo ancora alcuno` strumento di debug a co-adiuvare l’implementazione di un fragment o di un vertex program, la fase di validazione di uno shader pu`o rivelarsi esperienza frustrante.

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 95

Figura 4.19: Il fragment program per l’aggiornamento della posizione e la velocit`a delle masse. Si fa notare come in realt`a, pur essendo sintetizzati in un unico codice per ragioni di efficienza, idealmente si definiscano due shader diversi. Quale dei due verr`a attivato dipender`a variabile boolena pos stage

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 96

Figura 4.20: Schema dell’interazione tra GPU ed applicazione principale; la quale settando la variabile pos stage decide in quale stadio di integrazione si sia. Se pos stage == true si gl FragColor scriver`a sul frammento la posizione, altrimenti la velocit`a

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 97 Non poche sono le condizioni a cui lo stato della pipeline grafica deve sottostare per permettere ad uno shader di funzionare in maniera corret- ta. In questa sezione se ne esporranno le principali, quelle di cui si ha pi`u immediata esigenza. Nel corso della trattazione se ne introdurranno altre, dettate dal cambio della struttura implementativa di fondo. Si fa presente che tali condizioni non sono specifiche della catena di masse ma che riguar- dano globalmente qualsiasi simulazione fisica si intenda implementare su una GPU.

Si considereranno estranei alla trattazione quelli che invece sono errori implementativi nei driver dei costruttori dell’hardware, che pure non sono infrequenti. Nell’appendice A si trova testimonianza del vario scambio epi- stolare tra l’autore e l’Nvidia per la segnalazione di bachi nel driver e nel compilatore di OGLSL. A scanso di equivoci si vedr`a che L’Nvidia stessa riconoscer`a quei bug come propri.

Formato delle texture e dimensioni

In OpenGL le texture devono avere un numero di righe e di colonne che sia una potenza di due. Seppur recentemente siano stato introdotto delle estensioni che superino tale limitazioni, esse sono ancora troppo dipendenti dal costruttore della macchina per essere assunte come strumento per svi- luppare una libreria di calcolo su GPU cross-platform. Supponendo che si debba creare una texture per contenere n elementi si devono provvedere delle tessiture che abbiano le seguenti dimensioni:

col = min(N extP owOf T wo(n), N extP owOf T wo(M AX COLU M N )) (4.2)

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 98

row = N extP owOf T wo(ceil(n/N extP owOf T wo(M AX COLU M N ))) (4.3) Nelle due formule la funzione NextPowOfTwo dato un numero m resti- tuisce il numero successivo ad m che `e una potenza di due. La costante MAX COLUMN indica, invece, il numero di colonne massime che una tex- ture secondo le specifiche dell’API possa avere. Tipicamente questo valore in OpenGL `e di 4096. In seguito si evidenzier`a che costruire texture lunghe e strette genera un certo beneficio in termini di performance; si sar`a, per que- sto, costretti a considerare pi`u attentamente quale valore porre come limite massimo al numero di colonne per riga. La necessit`a di costruire texture di dimensioni prefissate costringe ad un piccolo ritocco dell’architettura dell’ap- plicazione. Precedentemente si `e imposto che gli strati della texture abbiano dimensioni uguali tra loro ed ora, per costruzione, si `e aggiunto il vincolo che righe e colonne di ogni strato siano in potenza di due. Purtroppo, per`o, questo non `e sufficiente, in quanto anche la texture delle masse nella sua totalit`a deve sottostare a tale condizione; il che impone di aver un numero di strati che a sua volta sia una potenza di due. Nella architettura per il sistema masse-molle finora definita ci`o non `e vero, in quanto si hanno tre attributi fisici distinti: posizione, velocit`a ed accelerazione. Si `e gi`a fatto notare che mantenere le informazioni sull’accelerazione sarebbe per ora superfluo ma si preferisce aggiungere un nuovo strato, che come vedremo, in futuro ci sar`a utile per mantenere la normale alla massa-vertice di un complesso simpliciale in tre dimensioni.

Sebbene fin dalla prima versione di OpenGL sia possibile passare texture di elementi in virgola mobile, in realt`a i valori appartenenti a tale insieme sono costruiti su soli 8 bit; assolutamente insufficienti per poter avere una

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 99

Figura 4.21: La struttura completa della texture delle masse. Si noti come sia stato aggiunto un nuovo strato per sottostare ai requisiti sulla potenza di due delle texture. Le celle colorate ombreggiate sono altres`ı necessarie per la stessa ragione

simulazione attendibile di un fenomeno fisico. Fortunatamente `e stata intro- dotta nel 2004 ARB texture float, una estensione allo standard di OpenGL che permette all’API di interagire con texture contenenti numeri a 32 bit per ognuno delle quattro componenti rgba. Il suffisso ARB sta ad indicare come tale estensione sia oramai supportata da tutti i costruttori e pronta ad essere standardizzata.

Quad di attivazione

Per attivare uno shader, come visto, `e necessario provvedere al disegno di un rettangolo dalle dimensioni dipendenti dal numero di frammenti su cui si

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 100 vuol effettuare il calcolo implementato nel fragment-program. La funzione glViewport(GLint x,GLint y,GLsizei width,GLsizei height) permet- te di determinare una regione rettangolare dello schermo all’interno della quale gli elementi tridimensionali da visualizzare verranno disegnati. I pa- rametri x ed y indicano il pixel che diventer`a l’angolo in basso a sinistra della finestra di renderizzazione; width e height il numero di pixel rispet- tivamente della larghezza e dell’altezza dell’area di rendering. Si fa notare che la glViewport non `e una funzione che ritaglia il contenuto di una por- zione del monitor (figura); essa, invece, definisce una funzione di mappatura tra un pixel della finestra principale ed uno dell’area rettangolare specificata dai parametri. In particolare se l’aspect ratio dello schermo `e diverso da width/height allora l’immagine finale risulter`a distorta (fig). La glViewport fornisce un comodo meccanismo per costruire un quad composto da un nu- mero prefissato di pixel, sar`a infatti sufficiente disegnare un rettangolo che occupi tutta la superficie della area delimitata dalla glViewport stessa. La funzione glOrtho(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top,GLdouble near,GLdouble far), oltre a costruire il volume di vista di una proiezione ortografica, permette di definire le coordinate tridimensionali degli estremi della finestra di rendering. A questo punto non resta che ricalcare i vertici del quad sugli angoli delimitanti l’area di disegno per ottenere il rettangolo di attivazione delle esatte dimensioni della finestra di viewport.

Avendo costruito la texture delle masse a strati si `e costretti di ricorrere al multitexturing per poter fornire ad ogni frammento le necessarie informazioni sui suoi attributi. Selezionare un livello all’interno della texture delle masse `

e operazione relativamente facile in quanto essendo le coordinate di texture comprese tra zero ed uno ed avendo quattro strati di attributi si ha che

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 101 data la cella contenente la posizione di una massa m di coordinate14 (x,y), la corrispondente velocit`a avr`a coordinate (x,y + 0.25), l’accelerazione si trover`a a (x,y + 0.5) ed, infine, la normale a (x,y + 0.75).

Per poter rendere agevole il recupero da parte di un frammento delle informazioni sullo stato dei suoi attributi vi `e la necessit`a di avere un rap- porto di 1:1 tra pixel del quad ed i texel della texture. Vi deve, cio`e, essere una corrispondenza diretta su come sono disposti i frammenti all’interno del rettangolo di attivazione e la posizione delle celle della tessitura. In altre parole, se la texture `e disposta su n colonne anche il quad dovr`a avere le stesse dimensioni.

Se, per`o, disegnassimo il rettangolo di attivazione semplicemente come definito in figura 4.22 (col e row sono stati definiti precedentemente nelle equazioni 4.4 e 4.5) l’implementazione del sistema masse-molle subirebbe una forte limitazione al grado della performance. Questo perch`e si attiverebbero un numero di frammenti potenzialmente molto superiore al numero effettivo delle masse, costringendo il fragment-processor a svolgere per ogni frammento h > #masse calcolo completamente inutile. Se ad esempio avessimo 2049 masse e le disponessimo su una texture di 1024 colonne avremmo bisogno di quattro righe per contenere le 4096 celle che formano la potenza di due successiva al numero delle masse effettive. Con il risultato che il cinquanta per cento del calcolo compiuto dal fragment-processor sarebbe stato sprecato. Per evitare di inficiare la performance dell’applicazione a causa dei vin- coli strutturali imposti da OpenGL `e quindi necessario costruire un’area di viewport, e quindi un quad di attivazione, che sia il pi`u possibile aderente al numero di masse componenti il sistema. In particolare, per ora, si far`a in

14Si ricorda che tipicamente le coordinate delle texture hanno forma (colonna, riga) al contrario di quanto si `e soliti fare per l’accesso ad una normale matrice.

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 102 glViewport(0.0,0.0,col, row); glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0); glBegin(GL_QUADS); glMultiTexCoord2f(0,0.0,0.0); glMultiTexCoord2f(1,0.0,0.25); glMultiTexCoord2f(2,0.0,0.50); glMultiTexCoord2f(3,0.0,0.75); glVertex2f(0.0,0.0); glMultiTexCoord2f(0,1.0,0.0); glMultiTexCoord2f(1,1.0,0.25); glMultiTexCoord2f(2,1.0,0.50); glMultiTexCoord2f(3,1.0,0.75); glVertex2f(1.0,0.0); glMultiTexCoord2f(0,1.0,0.25); glMultiTexCoord2f(1,1.0,0.50); glMultiTexCoord2f(2,1.0,0.75); glMultiTexCoord2f(3,1.0,1.0); glVertex2f(1.0,1.0); glMultiTexCoord2f(0,0.0,0.25); glMultiTexCoord2f(1,0.0,0.50); glMultiTexCoord2f(2,0.0,0.75); glMultiTexCoord2f(3,0.0,1.0); glVertex2f(0.0,1.0); glEnd();

Figura 4.22: Coordinate per il multitexturing del quad di attivazione. Versione inefficiente, si attivano molti pi`u frammenti del necessario

CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 103 modo di tagliar via tutti quei frammenti che si riferiscono a righe di texture che non conterranno alcuna massa. Per far ci`o sar`a sufficiente ridefinire il numero di righe componenti il quad nel seguente modo:

not empty row = ceil(n/M AX COLU M N )) (4.4) In seguito per migliorare ancora l’efficienza del calcolo si interver`a sul valore MAX COLUMN per minimizzare il numero di fragment che pur ri- candendo all’interno di una riga che contiene almeno una massa, dovrebbero restare comunque inattivi. Si fa presente, infatti, che si avessero 1025 masse disposte su colonne da 1024 celle, si otterrebbero due righe non vuote di cui la prima completamente piena mentre la seconda contente un unica mas- sa; con il risultato che avremmo anche con questo accorgimento uno spreco paragonabile a quello dell’esempio precedente.

Dovendo rispettare il vincolo di rapporto diretto tra pixel e texel la mo- difica delle dimensioni del quad imporr`a di dover modificare la definizione delle coordinate di texture. In particolare esse diverranno come da figure 4.23 e 4.24. In figura 4.25 infine `e visualizzato il rapporto tra la percentuale di righe di texture not empty e le dimensioni totali della texture stessa.

Documenti correlati