Algoritmi per la gestione della dinamica di complessi simpliciali in GPU

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download "Algoritmi per la gestione della dinamica di complessi simpliciali in GPU"

Transcript

1 Facoltà di Scienze Matematiche Fisiche e Naturali Corso di Laurea Specialistica in Informatica Algoritmi per la gestione della dinamica di complessi simpliciali in GPU Candidato Guido Ranzuglia Relatori: Dott. Paolo Cignoni Controrelatore: Prof. Roberto Grossi Dott. Fabio Ganovelli Anno Accademico 2004/2005

2 alle pennellesse intrise del sudore di mio padre, a tutti i dolori che attanagliano le ossa di mia madre, all azzurro di quegli occhi che rappresentano la meta.

3 Innanzi tutto non posso esimermi dal ringraziare i componenti del Vcg group del CNR di Pisa che mi hanno accolto, con grande dispendio di energie e risorse, presso di loro per poter compiere questo lavoro di tesi. Un grazie particolare va a Fabio e al prof. Cignoni. Senza le loro idee, i loro consigli e la loro presenza fisica nulla sarebbe stato. Grazie anche a tutte le giovani leve che nell ultimo anno mi sono state sedute accanto: Andrea, Giuseppe, Valentino, Marco...e sicuramente ne dimentico qualcuno. Non certo Nico a cui è, per forza di cose, dedicato un GRAZIE con tutte le lettere maiuscole...

4 Indice 1 Concetti introduttivi La pipeline di rendering La prima generazione di Hardware grafico La prima scheda grafica Una possibile soluzioni alternativa Evoluzione del bus di trasferimento dati Transform & Lighting unit Dagli acceleratori grafici alle GPU Applicazioni, driver e API Pipeline grafica standard di OpenGL Limiti imposti dalla pipeline statica Vertex Shader Fragment Shader Ripercussioni dovute all introduzione degli shader Linguaggi di shading Caratteristiche principali di OGLSL Tipi pre-definiti e definibili Attributi Variabili pre-definite

5 INDICE 2 2 GPGPU CPU vs GPU: qualche numero Linee guida nello sviluppo hardware Very Large Scale Integration Banda di trasferimento e latenza Indirizzi tecnologici per massimizzare la computazione Considerazioni sulle scelte architettoniche della CPU La GPU come stream processor Meccanismi hardware per il controllo sul flusso d esecuzione GPGPU: Un esempio concreto Limiti e difficoltà del GPGPU Elementi di fisica e di integrazione numerica Mass spring Metodi di integrazione numerica Metodi di Runge Kutta Metodi impliciti Complessi simpliciali in GPU Elementi e complessi simpliciali Gestione della dinamica dei complessi simpliciali Catena di masse in CPU Simulazioni fisiche in GPU Parametri di input di un fragment program Risultato di una computazione Interazione tra applicazione principale e shader Tipi di dato Catena di masse in GPU

6 INDICE Masse e molle Difficoltà implementativa del modello Una nuova strategia di calcolo Organizzazione di dati e texture il vertex-program Il fragment-program Condizioni al contorno Aggiornare la texture delle masse Render to texture Look-up di una texture da vertex program Interazione con il mouse Terza dimensione Complessi simpliciali di dimensione uno Texture di connettività Accedere alla texture Massa Fragment-program Complessi simpliciali di dimensione due Fragment-program Complessi simpliciali di dimensione tre Eliminare simplessi Risultati sperimentali e sviluppi futuri Tabelle dei risultati Commenti sui risultati ottenuti Sviluppi futuri Conclusioni

7 INDICE 4 A Screenshot del simulatore 135 B Corrispondenza con la Nvidia 138 B.1 Bug nel compilatore B.2 Bug nel driver

8 Capitolo 1 Concetti introduttivi Lo sviluppo e la diffusione di hardware specializzato, destinato a supportare e coadiuvare la CPU nelle funzionalità di grafica, ha conosciuto nell ultimo decennio un deciso incremento che fonda, in ultima analisi, la sua ragione di essere su solide basi economiche. Non solo l industria dei videogiochi muove fatturati ormai paragonabili, se non superiori, a quelli dell industria cinematografica (che a sua volta, già dalla fine degli anni settanta, investe nel settore quantità ingenti di denaro) ma la necessità di simulazioni credibili ha ormai da tempo travalicato i limiti propri dell intrattenimento per diffondersi sempre maggiormente in campi come la medicina, la fisica, l ingegneria e l industria militare. Si è passati così in un tempo relativamente breve dalle workstation dedicate, immesse sul mercato a prezzi che le rendevano inaccessibili ad un bacino di utenza che andasse oltre i laboratori di ricerca, alle moderne schede grafiche che rappresentano la dotazione basilare di qualsiasi personal computer di oggi giorno. Tratteggiare brevemente i passi intermedi di questo viaggio e sottolinearne le conseguenze immediate a livello software derivatene sara l obiettivo preminente di questo capitolo. 5

9 CAPITOLO 1. CONCETTI INTRODUTTIVI La pipeline di rendering In computer graphics il termine rendering indica il processo tramite cui è possibile passare da una descrizione tridimensionale di una scena complessa (nella quale, ad esempio, siano stati definiti una camera virtuale, dei modelli 3D, i materiale di cui tali modelli sono composti ed una o più sorgenti luminose) ad un immagine bidimensionale della scena stessa facilmente rappresentabile sullo schermo di un pc. Fine ultimo di ogni hardware dedicato alla grafica è di velocizzare in maniera significativa tale processo di renderizzazione 1 di modo da permettere un interazione confortevole con la successione di immagini che appaiano sul monitor. Si è soliti fissare il limite minimo di tale soglia di comfort sui 10 fps 2. Sotto suddetto limite sopraggiunge una difficoltà oggettiva da parte dell utente a rapportarsi con quanto gli viene mostrato sullo schermo; difficoltà che non di rado sfocia in un vero e proprio disagio fisico. È, altresì, interessante notare come, d altro canto, oltre i 70 fps l occhio umano non riesca a percepire sostanziali variazioni di frame-rate. Negli anni vari algoritmi e varie tecniche sono state definite per risolvere il problema del rendering; un approccio che si è dimostrato vincente nell ambito della grafica 3D con real-time si basa sul tassellare in un insieme di poligoni (tendenzialmente triangoli) le entità geometriche che compongono la scena tridimensionale e di dare tale insieme in pasto ad una pipeline grafica. Nella pipeline le diverse unità lavorano in parallelo elaborando, in un dato istante, ognuna una porzione di input diversa lungo il percorso di trasformazione da triangoli tridimensionali a pixel sullo schermo. È importante notare come nessuna assunzione sia fatta su dove tale pipeline sarà effettivamente 1 Non a caso le prime schede grafiche venivano descritte con la locuzione acceleratori grafici. 2 frame per second: numero di immagini al secondo generate dal processo di rendering

10 CAPITOLO 1. CONCETTI INTRODUTTIVI 7 eseguita; quello che si sta definendo è un ente concettuale per la risoluzione di un dato problema. Ovviamente quanti più stadi saranno lasciati alla CPU tanti più cicli di clock della CPU stessa saranno consumati per permettere il rendering della scena; con la conseguenza che tale tempo di calcolo si andrà a sommare con quello dovuto alla normale elaborazione dei dati dell applicativo stesso. Nel caso di implementazione in hardware di un segmento del processo di rendering è invece possibile, per la durata di tale stadio, eseguire in parallelo la computazione che ha luogo sull unità centrale con la computazione affidata al settore di pipeline in questione. Sarà scelta e compito dei costruttori dell hardware stesso decidere quali e quanti stadi implementare su un supporto specifico e quali demandare alla CPU. Tale affermazione porta con se, come conseguenza, la necessità di una interfaccia software tra la pipeline di rendering e l applicazione che si sta progettando, di modo da rendere trasparente al programmatore quante e quali parti di pipeline grafica siano effettivamente implementate su supporto dedicato e quante siano ancora demandate alla CPU. Tre sono i blocchi principali di una pipeline grafica(fig. 1.1) : - lo stadio dell applicazione da cui escono i triangoli tridimensionali che costituiscono la scena da rappresentare - lo stadio geometrico dove tali triangoli vengono mappati in maniera coerente(prospettiva,posizione della camera) in coordinate 2D di schermo - lo stadio finale di rastering in cui tali primitive sono scomposte in frammenti a cui viene assegnato un colore per essere tradotte in immagini memorizzabili in un color-buffer 3. 3 Il color-buffer è una zona di memoria con un numero di celle pari al numero di pixel

11 CAPITOLO 1. CONCETTI INTRODUTTIVI 8 Di norma tali blocchi saranno suddivisi al loro interno in un numero variabile di sottoblocchi a cui viene affidata solo una porzione della computazione eseguita dallo stadio di pipeline suddetto. Figura 1.1: Pipeline di rendering minimale 1.2 La prima generazione di Hardware grafico La prima scheda grafica La tendenza che è andata affermandosi negli anni è stata quella, come ovvio, di portare quante più porzioni di pipeline possibile sul supporto grafico dello schermo, dove in posizione (x,y) verrà inserito il corrispettivo colore che il pixel (x,y) dovrà avere sul monitor del PC

12 CAPITOLO 1. CONCETTI INTRODUTTIVI 9 dedicato. Nel 1995 la 3dFx immise sul mercato Voodoo, universalmente riconosciuta come il primo acceleratore grafico low-cost della storia. La Voodoo si faceva carico al suo interno dello stadio di rastering (fig. 1.2). Se di primo acchito puo sembrare sorprendente scoprire che non sia stato lo stadio di trasformazione della geometria e di illuminazione ad essere implementato ad hardware per primo, la cosa diventa decisamente più logica se si riflette su quanto in realtà sia piu pesante in una applicazione real-time (come un videogioco) la fase di rasterizzazione, in cui è frequente che un numero di frammenti maggiori della totalità dei pixel dello schermo necessitino di essere processati per determinare il proprio colore. Lo stadio geometrico era lasciato alla CPU che inviava, tramite bus Peripheral Component Interconnect (PCI), triangoli bidimensionali alla memoria locale alla scheda grafica; qui sarebbero stati presi in consegna dal rasterizzatore che avrebbe provveduto a partizionare ogni triangolo 2D in una moltitudine di frammenti ed, inoltre, avrebbe associato a ciascuno di essi un valore di profondità prospetticamente coerente per determinarne successivamente la visibilità. La Texture unit assegnava, quindi, ad ogni fragment la corrispettiva porzione di texture, interpolando i valori associati ai vertici del triangolo cui il frammento stesso apparteneva. Compito, infine, della raster operation unit (fig. 1.3) era quello di determinare come effettivamente il singolo fragment avrebbe influenzato l immagine finale contenuta nella porzione del frame buffer riferita come color-buffer. Sempre all interno del frame-buffer era presente, inoltre, un altra area di memoria, denominata z-buffer, il cui scopo era quello di contenere informazioni utili per arbitraggio della visibilità tra frammenti nel caso che due o piu

13 CAPITOLO 1. CONCETTI INTRODUTTIVI 10 Figura 1.2: Architettura della Voodo3 della 3dfx di essi avessero concorso per l assegnazione di un singolo pixel dello schermo Una possibile soluzioni alternativa Sebbene le mesh triangolari, come accennato in precedenza, siano la forma più comune di rappresentazione di un oggetto tridimensionale questo non significa, ovviamente, che non ce ne siano altre. In molti pacchetti applicativi CAD la geometria di un elemento 3D è definita da superfici matematiche; nella maggior parte dei casi Non-Uniform Rational B-Splines meglio note come NURBS. Oltre ad una moderatissima occupazione di memoria le NURBS presentano l innegabile vantaggio di poter descrivere superfici smooth senza dover ricorrere, come accade con le mesh triangolari, ad un aumento considerevole della complessità geometrica della scena. Non deve, quindi, sor- 4 Se un nuovo frammento si fosse sovrapposto ad uno già presente nel color-buffer, quest ultimo o veniva sovraimpresso oppure, nel caso di trasparenza, concorreva al colore finale del pixel conteso.

14 CAPITOLO 1. CONCETTI INTRODUTTIVI 11 Figura 1.3: Stadio di rastering prendere che in un passato non troppo remoto (1996) l Nvidia avesse deciso di basare le sue prime schede grafiche su una particolare implementazione delle NURBS stesse: le quadratic texture maps. Come il nome suggerisce le quadratic texture maps erano l implementazione di una generica superficie quadratica di equazione: ax 2 + by 2 + cz 2 + dxy + exz + fyz + gx + hy + iz + j = 0 (1.1) Il sostantivo texture sta indicare come non ci fosse alcuna distinzione formale tra la geometria e la tessitura che ne avrebbe dovuto arricchire l aspetto. La texture, cioè, non era attaccata sopra il modello tridimensionale, come avviene normalmente, ma essa stessa delimitava i contorni dell oggetto 3D che si andava a visualizzare. A dispetto di un eleganza matematica e di una resa effettiva superiore a qualsiasi altra periferica grafica dell epoca la

15 CAPITOLO 1. CONCETTI INTRODUTTIVI 12 soluzione non ebbe seguito principalmente per una sorta di divergenza temporale con le linee guida nello sviluppo del software multimediale di quegli anni. Nello stesso periodo della NV1, infatti, si andarono ad affermare API grafiche come Direct3D ed OpenGL entrambe orientate alla gestione di poligoni. È superfluo specificare come applicazione scritte sfruttando tali librerie non ricevessero alcun beneficio da schede grafiche che perseguivano filosofie totalmente diverse. Sebbene l NVidia fosse talmente convinta della bontà del suo approccio (tanto da rischiare il fallimento economico per produrre una seconda generazione di schede quadratic oriented) è convinzione oggi comune che sia stato un bene per la storia della computer graphics che tale approccio non abbia avuto successo. Non solo i triangoli sono entità matematiche più semplici e quindi più facilmente gestibili rispetto alle NURBS, ma soprattutto senza alcuna distinzione tra texture e geometria sarebbe stato estremamente difficile creare algoritmi generali per fare clipping dei vertici non visibili; senza contare, infine, che una volta spezzata una quadratica genera due superfici geometriche di grado superiore; le quali, quindi, non avrebbero potuto beneficiare dei vantaggi offerti dalla scheda grafica stessa Evoluzione del bus di trasferimento dati Nel 1998 nvidia, oramai scesa a piu miti consigli, ed ATI, introducono rispettivamente la TNT e la Rage. Oltre alla capacità di poter influenzare il colore di un pixel basandosi su più di una texture (multi-texturing) queste due nuove classi di acceleratori grafici erano le prime a sfruttare un nuovo tipo di bus per velocizzare il trasferimento dei dati dalla memoria principale alla memoria dedicata del supporto hardware. Si passava così dal PCI, a connessione parallela e condivisa tra più periferiche, alla AGP con connessione

16 CAPITOLO 1. CONCETTI INTRODUTTIVI 13 seriale (protocollo più semplice quindi scalabile) dedicata esclusivamente alla comunicazione con la scheda grafica e con una memoria propria che, pur non essendo locale alla scheda video permette a quest ultima, ad esempio, di fare look up delle texture. La velocità base di una comunicazione AGP tra CPU e hardware grafico (e solo in questa direzione) è due volte quella consentita dalla normale PCI. Storia di oggigiorno è l affermarsi di un nuovo standard: la PCI-Express; seriale e dedicata come la AGP ma che consente una velocita sedici volte superiore della PCI originale (quindi il doppio rispetto ad una AGP8x) e non a senso unico, cioè full-band sia da CPU verso la scheda video sia nel percorso inverso Transform & Lighting unit Tra il 1999 ed il 2000 tramite la definizione della Transform & Lighting Unit (T&L) all interno di schede come la GeForce2 o la ATI 7500, anche lo stadio geometrico venne spostato dalla CPU sull acceleratore grafico; liberando, così, definitivamente il processore centrale dal carico di calcolo dovuto all esecuzione dei blocchi della pipeline standard. In figura 1.4 è presentata una tipica T&L unit. La commercializzazione su larga scala dei sopracitati modelli segna la fine di una fase evolutiva importante nella definizione delle caratteristiche del moderno hardware grafico. Le funzionalità dell acceleratore grafico vengono sfruttate appieno sia dalla comunità scientifica che dalle case produttrici di software (modellatori e videogame) delineando e suggerendo nuove direzioni verso lo sviluppo di un chipset dedicato alla grafica completo e programmabile.

17 CAPITOLO 1. CONCETTI INTRODUTTIVI 14 Figura 1.4: Transform and Lighting unit 1.3 Dagli acceleratori grafici alle GPU Per poter meglio comprendere ed apprezzare le potenzialità insite nella successiva generazione di hardware grafico è opportuno osservare con maggior grado di dettaglio sia come avvenga l interazione tra una applicazione grafica ed il device sottostante, sia cosa accada allorquando tale interazione termini e il supporto hardware è pronto ad assumersi l onere di completare il processo di rendering Applicazioni, driver e API Tipicamente qualsiasi applicazione che sfrutti una componente hardware del calcolatore è, di fatto, suddivisa in due sottosistemi: la parte dell applica-

18 CAPITOLO 1. CONCETTI INTRODUTTIVI 15 zione vera e propria (che si occupa della logica del programma e che concettualmente e indipendente dallo specifico supporto fisico su cui tale processo venga eseguito) e la parte a cui si è soliti riferirsi con il termine di driver, il cui compito preminente è quello di presentare i dati che il device dovrà elaborare in una forma comprensibile al device stesso. È importante notare come il driver da solo non sia sufficiente a rendere l applicazione hardware independent. Per fare ciò è necessaria la presenza di un ulteriore strato software (Application Programming Interface) che implementi una interfaccia astratta, comune a tutta un intera classe di supporti hardware corrispondenti. Nel nostro caso i supporti hardware da astrarre sono, ovviamente, tutte le varie tipologie di schede grafiche che le industrie del settore immettano sul mercato. Come abbiamo precedentemente accennato, OpenGL e Direct3D sono divenute, dalla metà degli anni 90, la base per lo sviluppo di applicazioni grafiche real-time. Sebbene nell approccio presentino sostanziali differenze (Direct3D, più che rimanere un API vera e propria, come accade per Open- GL, è effettivamente un Software Development Kit) le due filosofie, comunque, vivono di forti punti di contatto. In ultima istanza la vera differenza tra le due API è che mentre OpenGL resta system operating independent, Direct3D, essendo diretta filiazione della Microsoft, è totalmente Windows oriented. Per il resto della trattazione si assumerà OpenGL come Application Programming Interface di riferimento Pipeline grafica standard di OpenGL L insieme delle funzionalità che OpenGL espone al programmatore, rappresentano un astrazione ad alto livello di una particolare pipeline grafica. Tali

19 CAPITOLO 1. CONCETTI INTRODUTTIVI 16 funzioni non si limitano solo al passaggio dei dati (tipicamente geometria e texture) dall applicazione al supporto sottostante destinato a completare il processo di rendering, ma includono anche procedure per alterare, influenzare e controllare lo stato della pipeline stessa. In ultima istanza si può considerare OpenGL come una macchina a stati per l aggiornamento del contenuto di un frame-buffer, costruita su un architettura client-server. Lo schema 1.5 rappresenta una buona approssimazione di quali siano le operazioni previste dal processo di renderizzazione implementato da OpenGL e in quale ordine tale operazioni avvengano. Per evitare di addentrarci troppo in aspetti tecnici che sviino dalle reali necessità di comprensione del lavoro in esame, la trattazione successiva si concentrerà solo sulle unità riportate nella figura. I restanti frammenti di pipeline, seppur rivestano un ruolo fondamentale nella definizione e nel completamento del processo di rendering, non aggiungono dettagli significativi in termini di astrazione del processo stesso. Figura 1.5: Pipeline grafica standard di OpenGL OpenGL permette di definire per ogni vertice cinque tipologie differenti di attributi: posizione, normale al vertice (usata principalmente per il calcolo

20 CAPITOLO 1. CONCETTI INTRODUTTIVI 17 della luce incidente sul vertice stesso), colore, informazione sul materiale di cui e composto e coordinate relative alla texture che andrà ad arrichire la descrizione visiva della mesh tridimensionale. Tali attributi possono essere definiti tramite una delle tre modalità differenti previste dall API: Enumerazione diretta degli attributi componenti tutti i vertici di una singola primitiva grafica (punti, linee, triangoli, poligoni generici) per mezzo delle funzioni glvertex, glnormal, glcolor, glmaterial e gltex- Coord. Impacchettamento in un array per attributo di tutti i vertici componenti una intera mesh tridimensionale e passaggio successivo del riferimento del vettore ad OpenGL (meccanismo dei vertex-array). Definizione di liste precompilate di comandi OpenGL con il metodo delle display-list. Al loro interno le display-list possono contenere sia invocazioni di funzioni riconducibili alla modalità un vertice alla volta sia la definizione di vertex-array. Essendo basato sulla pre-compilazione dei comandi tale metodo presenta la forte limitazione di necessitare che gli attributi non cambino di valore durante l esecuzione dell applicazione. Tali dati, entrati a far parte della memoria controllata dall applicazione, potranno essere effettivamente allocati sia nella memoria centrale del calcolatore sia in quella locale alla scheda grafica. Sebbene, grazie a recenti introduzioni alle specifiche di OpenGL, sia effettivamente possibile tentare di forzare l allocazione sul dispositivo hardware dedicato, nel proseguimento della trattazione considereremo la questione come marginale e completamente trasparente al programmatore.

21 CAPITOLO 1. CONCETTI INTRODUTTIVI 18 Per-Vertex Operations (2) A questo punto indipendentemente da quale delle tre modalità sopraelencate si sia prescelta, OpenGL attiva la sua prima sezione di pipeline: per-vertex operations. Come lascia facilmente intuire il nome in questo stadio una serie di operazioni prefissate vengono applicate ad ogni vertice che fluisce attraverso di esso. La maggior parte di queste operazioni riguarda la trasformazione dei punti constituenti la scena tridimensionale da uno spazio vettoriale ad un altro. Per semplificare la trattazione successiva si ritiene necessario introdurre un esempio concreto, sviluppato passo passo. Si supponga di voler disegnare un quadrato come quello in figura 1.7.a e di definire la geometria in opengl tramite la modalità diretta: glbegin(gl_quads); glvertex(0.0,0.0); glvertex(1.0,0.0); glvertex(1.0,1.0); glvertex(0.0,1.0); glend(); Figura 1.6: Come si può notare dalla figura tutti gli altri vertici sono stati definiti rispetto al vertice A, assurto ad origine de sistema di riferimento locale nel quale è stato modellato il quadrato. In letteratura si è soliti riferirsi a tale sistema di riferimento con il nome di object-space. Si voglia, a questo punto, ruotare il quadrato di 45 rispetto al suo punto centrale; di modo da fargli assumere una connotazione romboidale. Per far

22 CAPITOLO 1. CONCETTI INTRODUTTIVI 19 Figura 1.7: Esempio di rotazione di un quadrato. La seconda immagine della serie presenta una rotazione attorno ad un asse che non è il centro del quadrato stesso. questo è necessario introdurre un nuovo sistema di coordinate: world-space. Come suggerisce il nome tale spazio simula il nostro mondo virtuale, in cui tutte le varie istanze di mesh tridimensionale verranno a convivere ed interagire tra di loro. Anche world space ha, come ovvio, la sua origine, il suo punto (0,0). Se si mappasse, semplicemente, con la funzione identità le coordinate di object-space in quelle di world-space si otterrebbe qualcosa di simile a quanto riportato in figura 1.7.b. Poichè in OpenGL tutte le rotazioni avvengono attorno all origine del mondo, ciò non permetterebbe di muovere il quadrato in maniera coerente con quanto ci si era prefissati. È quindi necessario prima di tutto spostare l origine del mondo in modo da farla coincidere con il centro di rotazione del quadrato (fig. 1.7.c). Ora sarà sufficiente ruotare il quadrato per ottenere quanto voluto(fig. 1.7.d). Quindi la funzione di mappatura per inserire il nostro quadrato in world-space nella giusta connotazione romboidale, prevederà, sicuramente, la composizione di una traslazione dello spazio vettoriale seguita da una rotazione del nuovo sistema di coordinate ottenuto. Trasformazioni affini (rotazioni, traslazioni e scalatura) vengono rappresentate in OpenGL per mezzo di matrici 4x4. La composizione di più trasformazioni è ottenuta dalla moltiplicazione delle matrici rappresentanti singole operazioni di trasformazione. Portare un punto

23 CAPITOLO 1. CONCETTI INTRODUTTIVI 20 tridimensionale dallo spazio di coordinate dell oggetto allo spazio di coordinate di mondo implica quindi, come già accennato in precedenza, il dover moltiplicare tale punto per una matrice di trasformazione corrispondente. Una volta composta la scena tridimensionale è necessaria una nuova trasformazione del sistema di riferimento: da world-space ad eye-space. Eyespace rappresenta lo spazio di coordinate con origine nel punto in cui è piazzata la camera virtuale che dovrà inquadrare la scena. Varie sono le ragioni per cui è necessario questo ulteriore cambio di frame, la più importante delle quali è che nella pipeline standard di OpenGL il calcolo della luce incidente su ogni singolo vertice si basa sulle coordinate espresse nello spazio di camera. Anche questa ulteriore trasformazione avviene tramite moltiplicazioni matriciali. Benchè alcune API 3D tengano separate la matrice di world-space da quella di eye-space, OpenGL condensa le due in una matrice nota come model-view; ciò implica di fatto che dopo la moltiplicazione con tale matrice un punto passi direttamente da object-space nello spazio della camera. Una volta in eye-space, come già accennato prima, per ogni vertice ha luogo il calcolo di come le fonti luminose definite dal programmatore influenzino il colore finale del vertice stesso. Riveste un ruolo centrale in tale computazione (basato sul modello di illuminazione di Phong) la normale definita per il singolo punto tridimensionale. Parallelamente al calcolo dell influsso della luce, ha luogo un ulteriore trasformazione atta a definire il volume di vista della camera virtuale. Tale volume sarà rappresentato da un parallelepipedo nel caso di una proiezione ortogonale, da un tronco di piramide nel caso di una, naturale, visione prospettica. Anche questo cambio di coordinate, avvenga per mezzo di un ulteriore matrice, nota in OpenGL come Projection-matrix. In successive sezioni di pipeline i vertici facenti parte di primitive che ricadano fuori dal volume di

24 CAPITOLO 1. CONCETTI INTRODUTTIVI 21 vista verranno scartati, esentando, così, OpenGL dal carico di calcolo dovuto a punti che non influenzeranno l immagine che alla fine del rendering sara impressa nel frame buffer. Per questa ragione lo spazio vettoriale in cui sono stati trasformati i vertici dalla matrice di proiezione è noto come clip-space. Poichè, come si è appena visto, le operazioni principali che avvengono nella per-vertex unit sono le trasformazioni nei vari sistemi di riferimento e il calcolo dell illuminazione, si è soliti chiamare l unità preposta a tali compiti Transformation & Lighting unit. Primitive Assembly (3) e Primitive Processing (4) Nello stadio di Primitive Assembly, come il nome suggerisce, i vertici facenti parte del flusso uscente dall unità di Trasformation & Lighting vengono ri-assemblati per comporre le primitive geometriche definite dal programmatore. I punti richiedono un singolo vertice, le linee due, i triangoli, come è ovvio, tre, mentre un generico poligono, altrettanto ovviamente, sarà composto da un numero arbitrario di vertici. Compito dell unità successiva, Primitive Processing, è quello di testare se la primitiva in esame ricada all interno del volume di vista. Nel caso che solo una porzione della primitiva stessa viva all interno del frustum predefinito, la pipeline si farà carico spezzarla in due parti; ovviamente solo su quella appartente al volume suddetto verranno effettuate le restanti operazioni previste dal processo di rendering. Inoltre dovere dell unità di Primitive Processing sarà quello di mappare i punti tridimensionali in oggetti 2D rappresentabili sul monitor di un calcolatore, coerentemente al tipo di proiezione (ortogonale o prospettica) che si sia scelto.

25 CAPITOLO 1. CONCETTI INTRODUTTIVI 22 Rasterizzazione (5) Fino a questo momento le primitive geometriche sono ancora rappresentate solo dai vertici che ne delineano i punti estremi. Riempire lo spazio all interno di tali confini con una serie di minuscoli frammenti delle stesse dimensione dei pixel componenti il frame buffer, è dovere dell unità di rastering. Come già detto un vertice non si compone solo di un punto nello spazio ma anche di attributi come colore, coordinate di texture e materiali. Compito del rasterizzatore è determinare tali valori per ogni frammento interpolando i corrispettivi attributi ai vertici della primitiva a cui il frammento appartiene. Nulla viene fatto per quanto riguarda la normale in quanto nella pipeline standard di OpenGL l illuminazione ha luogo soltanto ai vertici (Goraud Shading). Gli effetti della luce saranno estesi ai singoli frammenti semplicemente interpolando il colore dei vertici derivato dall influsso delle fonti luminose. Fragment Processing (6) L operazione principale che avviene in questo stadio è l accesso in memoria texture per ottenere la porzione dell immagine destinata al singolo frammento. Il texel ottenuto potrà (dipendentemente dallo stato in cui si venga a trovare la pipeline) essere interpolato con il colore del framento per determinarne l aspetto definitivo o, altrimenti, ricoprirlo completamente. In realtà texturizzare un oggetto tridimensionale presenta molti più aspetti di quanto si sia precedentemente accenato. Il mip-mapping, ad esempio, in quanto correlato direttamente ad una delle problematiche della mia tesi, sarà introdotto al momento opportuno.

26 CAPITOLO 1. CONCETTI INTRODUTTIVI 23 Per-Fragment Operations (7) Sebbene anche il texturing sia un operazione che avviene per ogni singolo frammento per ragioni che saranno più chiare in seguito, tenderemo a tenere separate le due unità. La Per-Fragment Operations è l implementazione (generalmente e sperabilmente hardware) di una serie di test sullo stato di un frammento. Il più noto di questi è il depth test fondamentale per determinare se sullo schermo un fragment sia coperto da un altro. Se ciò dovesse avvenire due sono le possibilità: o il frammento viene scartato, o, nel caso di semitrasparenza, concorre alla determinazione del colore del pixel corrispondente. Frame-Buffer Operations (8) Nell ultimo livello della nostra pipeline (semplificata) di OpenGL si eseguono tutti quei test che abbiano per oggetto aree di memoria all interno del framebuffer. Di solito l unità di Frame-Buffer Operations è usata pesantemente per ottenere effetti grafici non banali (ombre, riflessioni speculari, segni cinetici... ) non direttamente supportati da OpenGL. Completata anche questa fase l immagine della scena tridimensionale è finalmente pronta per essere inviata sullo schermo. 1.4 Limiti imposti dalla pipeline statica Fino a poco tempo fa OpenGL si presentava come un interfaccia sufficientemente flessibile per venire incontro a buona parte delle esigenze dei programmatori di applicazioni grafiche tridimensionali; esigenze vincolate dai limiti oggettivi alla performance imposti dalle prime schede grafiche. Sebbene vari parametri potessero essere settati per influenzare computazioni che

27 CAPITOLO 1. CONCETTI INTRODUTTIVI 24 avevano luogo in uno stadio della pipeline, fino alla versione 1.4 dell API nè le operazioni fondamentali, nè l ordine in cui queste avvenivano potevano essere modificate. Si supponga, ad esempio, che si voglia cambiare il modello di illuminazione della scena, passando da quello di Phong (modello di illuminazione stadard di OpenGL) a quello, ad esempio, di Cook-Torrence. Far questo con un processo di renderizzazione statico implica disabilitare il calcolo degli effetti della luce sui vertici (calcolo che, è bene sottolineare, avveniva su una periferica hardware specializzata) ed effettuare tale computazione per ogni vertice sulla CPU; impiegando, così, un numero di cicli di clock proporzionale alla cardinalità degli elementi componenti lo stream di vertici. Il valore di colore ottenuto sarebbe poi stato inviato ad OpenGL tramite la funzione glcolor. Ovviamente tutto questo può portare ad un rapido decadimento della performance. 1.5 Vertex Shader Nel con le versioni 3 e 4 della GeForce NVidia e con la ATI 8500, introducono (per la prima volta a livello di schede customer consumer) il concetto di programmabilità della pipeline di rendering, inserendo nelle proprie schede grafiche un per-vertex processor; un processore in grado, cioè, di interpretare una lista di comandi definiti dall utente ed applicarli su ogni singola unità facente parte di uno stream di vertici. La pipeline veniva quindi ad assumere un aspetto simile a quello riportato in figura 1.8. Idealmente (in pratica non è necessariamente detto che la cosa stia in questi termini) le operazioni previste dalla pipeline standard divengono esse stesse un programma che viene interpretato ed eseguito dal per-vertex

28 CAPITOLO 1. CONCETTI INTRODUTTIVI 25 Figura 1.8: Pipeline con vertex processor processor 5. È importante notare come non sia possible modificare solo un sottoinsieme delle operazioni che avvengono per vertice; non è, ad esempio, possibile voler modificare solo il modello di illuminazione e sperare di mantenere il codice che implementi le varie trasformazioni da uno spazio vettoriale all altro. Se si vuole una nuova formula per il calcolo della luce è necessario farsi carico anche dele funzionalità di mappatura fra i vari sistemi di riferimento. Operazioni tipiche che hanno luogo in un vertex shader comprendono le trasformazioni dei vertici, delle normali e il calcolo dell influenza delle fonti luminose, la generazione delle coordinate di texture ai vertici, la definizione di funzioni di distorsione della scena (si pensi ad esempio ada un vertex program che deformi un oggetto come se fosse visto attraverso una lente di ingrandimento) e l uso di una tecnica nota come displacement mapping che, rivestendo un ruolo centrale all interno di questo lavoro di tesi, ci si riserva di introdurla con maggior grado di dettaglio in seguito. È invece fondamentale notare immediatamente come sia impossibile defi- 5 È infatti garantito che uno shader che simuli la pipeline pre-fissata di OpenGL abbia la stessa performance della pipeline stessa.

29 CAPITOLO 1. CONCETTI INTRODUTTIVI 26 nire un vertex program il cui comportamento dipenda da un vertice diverso da quello elaborato in un dato istante sulla per-vertex unit. Non è possibile calcolare, ad esempio, il colore di un particolare vertice come media pesata di quelli a lui connessi. Questo perchè in uno stream-processor (come di fatto è una scheda grafica) non è predicibile l ordine in cui gli elementi dello stream verranno processati; non e quindi detto che quando io calcoli il colore del vertice nell esempio siano gia stati computati anche i rispettivi valori dei vertici che lo circondano. La ragione intrinseca di questa limitazione sulla classe degli algoritmi implementabili e nella struttura stessa che si è scelti per la definizione dell architettura delle moderne schede grafiche. Struttura che sarà al centro della trattazione del secondo capitolo. Con l introduzione del vertex shader i device grafici passano dall essere semplici acceleratori grafici a vere proprie Graphic Processor Unit (GPU) Fragment Shader Nel 2002 con la prima generazione di NVidia Fx e con la ATI 9000 anche lo stadio di fragment processing viene sostituito da una unità logica, il fragment shader, in grado di interpretare istruzioni da applicare ad ogni singolo frammento che fluisce attraverso la pipeline di rendering. Operazioni tipiche cha hanno luogo in un fragment shader definito dall utente possono essere: - Interpolazione dei valori calcolati ai vertici ed operazioni su tali valori - Calcolo di coordinate per l accesso alla memoria texture - Applicazione della texture - Operazioni di aggiunta e controllo della densita della nebbia

30 CAPITOLO 1. CONCETTI INTRODUTTIVI 27 Da un punto di vista prettamente grafico la scrittura di un fragment program presenta, indubbiamente, un maggior grado di libertà rispetto ad un programma operante per ogni singolo vertice. Come abbiamo visto nel paragrafo precedente gran parte tutti i vertex-program che si scrivono prevedono di ri-implementare buona porzione delle funzionalità già definite dalla pipeline standard. Nel caso di un programma operante per frammento, invece, il numero di effetti grafici raggiungibili è sorprendentemente elevato e vario. La serie di figure rappresenta solo alcuni dei moltissimi effetti ottenibili tramite la definizione di fragment program corrispondenti. Si ritiene fondamentale ricordare ancora una volta che, sebbene la maggior parte di tali risultati potesse essere raggiunta tramite la scrittura un analogo programma eseguibile sulla CPU, sfruttare appieno le potenzialità dell unita dedicata presenta, perlomeno, il vantaggio di permettere di scaricare l unità centrale da tale operazione, permettendogli così parallelemente di proseguire l elaborazione dei dati non grafici dell applicazione. Vedremo in seguito che i vantaggi effettivi non si limitano solo a questo. Valgono anche per il fragment shader le stesse considerazioni espresse poc anzi per il vertex shader. In particolare non è possibile sostituire solo una parte della funzionalità previste dalla pipeline standard di rendering per lo stadio di fragment processing e, più importante, non è possibile scrivere fragment program il cui funzionamento dipenda da frammenti diversi da quello che è in quel istante oggetto di computazione. Questo per le stesse ragioni di efficienza precedentemente enunciate.

31 CAPITOLO 1. CONCETTI INTRODUTTIVI 28 Figura 1.9: Pipeline con fragment processor Ripercussioni dovute all introduzione degli shader La definizione dei concetti di vertex e fragment processor ci permette di analizzare ad un alto livello di astrazione la struttura portante di una moderna scheda grafica. In figura e presentato uno spaccato della GeForce 6800 dell NVidia. La caratteristica che salta immediatamente agli occhi e la possibilità di poter accedere anche dal vertex program alla memoria texture. Sebbene di primo acchito questo possa apparire un dettaglio non particolarmente significativo, in realtà come vedremo ampiamente in seguito questo a dato luogo ad un uso sorprendente ed imprevisto delle schede grafiche. Senza di quello lo stesso lavoro in esame non avrebbe avuto senso di essere. Nel prossimo capitolo torneremo ad osservare piu in dettaglio le caratteristiche tecniche in termini di struttura e performance delle attuali schede grafiche oggi sul mercato. L introduzione del vertex e del fragment shader non ha significato solo un cambiamento dal punto di vista hardware ma anche un ripensamento dell architettura delle API sopra le quali le applicazioni grafiche vengono scritte. A

32 CAPITOLO 1. CONCETTI INTRODUTTIVI 29 titolo di sunto di quanto finora introdotto viene presentato in figura come si venga a modificare la pipeline grafica di OpenGL con l aggiunta del concetto di programmabilità dell hardware dedicato alla grafica tridimensionale. 1.6 Linguaggi di shading Un po di storia Il concetto di programmabilità dei device grafici è in realtà meno recente di quanto si possa credere. Già verso la fine degli anni ottanta su macchine high-end specializzate si introdussero le prime GPU per il calcolo e il controllo della computazione. Il costo eccessivo di tali macchine restringeva notevolmente il bacino di utenza limitato in quegli anni praticamente alla sola industria cinematografica. E comunque il fine di tale hardware non era quello di fornire interazione real-time ma bensì, cosa naturale in un film, di elaborare in fase di post-processing le sequenze del film stesso. Parallelamente alla definizione dello hardware nasce anche l esigenza di definire un formalismo di alto livello che permetta una più confortevole interazione con il supporto fisico sottostante. Nasce così nel 1988 RenderMan nei laboratori della Pixar ( non a caso...) il primo linguaggio di shading universalmente riconosciuto come tale. Che l utenza a cui è destinato RenderMan sia particolare lo si capisce subito dalle caratteristiche più evidenti del linguaggio.. Invece dei semplici vertex e fragment shader tale linguaggio presenta light shader, displacement shader, surface shader, volume shader e image shader. Ciò ha come immediata conseguenza il fatto che il moderno hardware grafico mal si adatti a supportare le feature introdotto da tale linguaggio. Nonostante questo nell ambito dell industria cinematografica RenderMan è ancora uno

33 CAPITOLO 1. CONCETTI INTRODUTTIVI 30 strumento oggigiorno ampiamente usato (per esempio nelle trilogie di Lord of the Rings e di StarWars). Caratteristiche simili a quelle di RenderMan presentava anche il primo linguaggio di shading costruito sopra OpenGL: Interactive Shading Language (ISL) che supportava light shader e volume shader. Ben più interessanti per lo sviluppo del lavoro in esame sono i tre linguaggi successivi, filiazione diretta dell architettura delle moderne schede grafiche: - High Level Shading Language (HLSL), prodotto dalla Microsoft - C for graphic (Cg), introdotto dalla NVidia - OpenGL Shading Language (OGLSL o anche GLSL), le cui specifiche sono definite dall OpenGL consortium). Concettualmente, a parte minime differenze di carattere sintattico, i tre idiomi sono pressochè uguali. La vera differenza risiede nell API (e di conseguenza nella pipeline) su cui i programmi definiti in tali linguaggi saranno eseguiti. OGLSL avrà ovviamente come base OpenGL, HLSL Direct3D, mentre Cg presenta un livello di astrazione maggiore che gli permette di poter utilizzare entrambi le API 6. Coerentemente con quanto fatto finora si continuerà ad assumere OpenGL come ambiente di sviluppo principe del lavoro in esame; scelta che ovviamente ci orienterà nell adottare OGLSL come linguaggio per la definizione degli shader implementati all interno di questa tesi. 6 Sebbene questo possa apparire come una importante nota a favore del linguaggio del NVidia, la realà dei fatti indica come sia proprio Cg quello che forse sarà destinato a scomparire per primo.

34 CAPITOLO 1. CONCETTI INTRODUTTIVI Caratteristiche principali di OGLSL OpenGL shading language è, innanzitutto, un linguaggio Turing-equivalente. Sebbene la precisazione possa apparire superflua, è invece necessario notare come tale condizione sia stata raggiunta recentemente. Per poter mantenere un grado di efficienza elevato, tramite la massimizzazione del parallelismo (su tale concetti ritorneremo diffusamente nel secondo capitolo), si era preferito nelle prime implementazioni hardware di evitare di esporre istruzioni per il controllo del flusso dell elaborazione. Si costringeva così il programmatore a definire shader program che avessero lo stesso comportamento su tutti gli elementi dello stream; comportamento che doveva essere indipendente dallo stato di ogni singolo vertice o frammento. In altre parole si rendeva implicita la sincronizzazione di tali unità; stesso numero e stesso tipo di istruzioni su uno stesso tipo di dato implica, ovviamente, che ogni elemento dello stream impiegasse un quantità di tempo pari agli altri per terminare la propria computazione. Ovviamente ciò rappresentava un vincolo troppo forte sulla tipologia di algoritmi definibili all interno degli shader. Per questo sono stati introdotte le usuali istruzioni di controllo del flusso presenti nella quasi totalità dei linguaggio di programmazione: if-then-else, while,for ed invocazione di funzioni. OGLSL ha un paradigma di programmazione prettamente imperativo e sebbene stilisticamente possa apparire simile al C con l aggiunta di alcune caratteristiche del C++ (overloading delle funzioni e costruttori) in realtà strutturalmente è, forse, più vicino alle prime implementazioni del Fortran che non facevano uso del concetto di stack delle chiamate. Inutile dire che ciò impedisca qualsiasi forma di ricorsione. Sono inoltre banditi anche i puntatori e l allocazione dinamica della memoria. L unico tipo di memoria disponibile per uno shader e, dunque, quella statica che elargisce il compilatore del

35 CAPITOLO 1. CONCETTI INTRODUTTIVI 32 linguaggio. Oltre all impossibilità della ricorsione ciò ha come conseguenza che esiste un tetto al numero massimo di istruzioni e alla profondità massima di chiamata delle funzioni. Il supporto che OpenGL offre per il proprio linguaggio di shading e composto fondamentalmente di due parti: - il linguaggio di shading vero e proprio (OGLSL) - integrazioni all API previa introduzione di funzioni per permettere di costruire un contesto in cui poter compilare e far eseguire gli shader scritti dall utente. OGLSL e supportata all interno di OpenGL dalla versione 1.5 come estensione ARB, anticamera alla piena integrazione che avverrà con la definizione delle specifiche di OGL Tipi pre-definiti e definibili In ultima istanza un linguaggio di programmazione non è altro che un algebra su un insieme di tipi. Anche i linguaggi di shading non sfuggono a questa caratteristica; è, quindi, fondamentale soffermarsi su quali siano i tipi supportati e quali i qualificatori definibili sui tipi stessi. Ovviamente sono presenti i soliti tipi scalari int e float e dal C++ sono ereditati i valori booleani. Una qualsiasi varibile di tipo T viene instanziata tramite il costruttore del tipo suddetto. Es. float a = float(1.0); int b = int(3); bool c = bool(true). Gli interi sono entità particolari in un linguaggio di shading. La loro presenza serve principalmente per indirizzare array e per implementare in maniera efficiente i cicli. Essendo il loro uso limitato non è infrequente che in maniera trasparente al programmatore questi vengano convertiti in float.

36 CAPITOLO 1. CONCETTI INTRODUTTIVI 33 Gli int sono sempre signed ed è, altresì, garantito che abbiano un ampiezza di almeno 16 bit. Il float è lo scalare per eccellenza dei linguaggi di shader e la loro implementazione è compatibile con quella definita dalla IEEE. In OGLSL non è previsto il concetto di cast implicito ed il compilatore in questo è abbastanza pedante. Se l argomento di una funzione è un float non vi sarà alcuna possibilità di accontentarlo passandogli un int. Esiste una sola deroga a questa regola: i costruttori. Scrivere una float c = float(1); e l unica forma di cast concessa dal linguaggio. Tipi caratteristici di un linguaggio di shading sono invece i vettori e le matrici: - vec2, vec3, vec4 sono vettori di 2/3/4 float - ivec2, ivec3, ivec4 sono vettori di 2/3/4 int - bvec2, bvec3, bvec4 sono vettori di 2/3/4 bool Per accedere ad una componente di un vettore è disponibile l usuale operazione di swizzle; per aiutare ad aggiungere semantica a quello che si sta selezionando sono presenti per una stessa coordinata più modi di referenziarla: -.x,.y,.z,.w (coordinate) -.r,.g,.b,.a (colore) -.s,.t,,p,.q (texture) Ovviamente e sempre possible accedere alla coordinata x di un vec4 rappresentate un punto p dello spazio con p.r o con p.s. I suffissi sono solo per favorire leggibilità del codice. Le matrici sono solo matrici quadrate di float e sono denotate dai tipi:

37 CAPITOLO 1. CONCETTI INTRODUTTIVI 34 - mat2 matrice 2x2 - mat3 matrice 3x3 - mat4 matrice 4x4 Sono accessibili sia come colonna, restituendo vec corrispondente alla dimensione della matrice, sia per singolo elemento scalare. E possibile definire array 7 con una sintassi del tutto simile a quella del C: vec4 point[10] indica un array contenente dieci vec4. Non esistendo allocazione dinamica della memoria e sempre necessario specificare la dimensione dell array stesso. Sono presenti le struct ed e quindi possibile definire conglomerati di tipi. Supponiamo, ad esempio, di voler definire un punto luce identificato da una posizione e dal colore della luce emessa: struct light { vec4 color; vec4 position; }; Per dichiarare una variabile di tipo light sara sufficiente scrivere light l = light(vec4(0.0),vec4(0.0)); che, senza che sia stato definito alcun costruttore per light da parte dell utente, inizializza i membri della struct secondo l ordine di passaggio dei parametri. L uso del tipo void e consentito per le sole funzioni che non prendono e/o non restituiscono valori ma non è consigliabile usarli... è stato spesso notato nello svolgimento del lavoro in esame come il loro comportamento fosse alquanto aleatorio.

38 CAPITOLO 1. CONCETTI INTRODUTTIVI 35 È infine presente un ultimo tipo il cui scopo è facilitare l accesso in lettura ad una texture: sampler. Sono definiti: - sampler1d, sampler2d, sampler3d funzioni per l accesso ad una texture n-dimensionale - samplercube per l accesso alle cube - sampler1dshadow e sampler2dshadow per l accesso ad una depth texture Attributi Gli attribuiti sono parte fondamentale di qualsiasi linguaggio di shading. In OGLSL si riconoscono principalmente tre tipi di attributi: uniform, attribute e varying. Tutti e tre indicano fondamentalmente una classe di comunicazione o tra applicazione-shader o tra vertex shader-fragment shader. Una variabile dichiarata attribute e intesa come una variabile che conterra valori passati allo shader dalla applicazione e che tale valore e specifico di un particolare vertex o fragment. Una variabile dichiarata uniform e intesa come una variabile che conterrà valori passati allo shader dalla applicazione e che tale valore è comune ed immutato per tutti i vertici o frammenti di quella passata di rendering. Una variabile definita varying e una variabile contenente un valore passato dal vertex program al fragment program. Il fragment program ne riceverà un interpolazione del valore calcolato ad ogni vertice del poligono a cui tale frammento appartiene 8. 8 È, invece, impossibile passare qualsiasi valore indietro dal fragment program al vertex program.

39 CAPITOLO 1. CONCETTI INTRODUTTIVI 36 Una variabile attribute in realta non e altro che un parametro che un particolare vertice o frammento passa ad uno shader per influenzarne il calcolo. In OGLSL si preferisce, però, scrivere funzioni che non prendano argomenti e che presentino, invece, una serie di dichiarazioni attribute in cima al file 9 Per settare varibili di tipo uniform e attribute esistono funzioni ARB in OpenGL da invocare dentro l applicazione. Le variabili di qualificatore varying non ne hanno bisogno in quanto sono sempre introdotte e definite. solo all interno di un vertex shaderd. È definito, infine, il qualificatore const, il cui significato e immediatamente intuibile Variabili pre-definite Esistono all interno del linguaggio un certo numero di variabili pre-definite il cui significato ed utilizzo è riservato per compiti speciali. La più riconoscibile di queste, gl Vertex, è la variabile contenente la posizione di un vertice, passata alla scheda grafica tramite la funzione glvertex dell API standard. Ovviamente questa non e altro che la variabile attribute destinata a contenere le coordinate del vertice in world-space così come sono state determinate dall applicazione. La gl Position e una variabile di tipo varying a cui deve essere necessariamente assegnato un valore in un vertex shader; pena la notifica di un errore 9 Questo principalmente per ragioni di efficienza. Se si volesse, ad esempio, scrivere uno shader che necessiti di un parametro e tale parametro fosse necessario passare ad un altra funzione definita nello shader avrei bisogno di un ulteriore copia del valore. Ciò puo inficiare pesantemente le prestazioni in quanto, come si è visto, non esistendo i puntatori tutti i passaggi di parametri avvengono per copia. Definendo tale parametro come globale sacrifico una dose ragionevole di pulizia programmativa in favore di un aumento della performance.

40 CAPITOLO 1. CONCETTI INTRODUTTIVI 37 da parte del compilatore. Essa conterrà (di solito) le coordinate del vertice trasformate in eye-clip space. È, fortunatamente, presente tra le procedure pre-definite dal linguaggio la funzione ftransform che modifica secondo le matrici di ModelView di Projection la posizione (gl Vertex) del vertice presente in un dato istante all interno del vertex processor. Altra variabile a cui è necessario assegnare un valore e la gl FragColor nel fragment shader. Essa identifica il colore che il pixel dovra avere sullo schermo, gl FragColor = vec4(1.0) colorera, ad esempio, ogni pixel rasterizzato di bianco. Come queste sono presenti moltissime altre variabile (gl Color, gl Normal,gl ModelViewMatrix... ) il cui significato è sufficientemente intuitivo da non necessitare di alcun commento.

41 Capitolo 2 GPGPU Nel capitolo precedente ci si è principalmente concentrati nel definire, per sommi capi, i concetti centrali che sono alla base della pipeline grafica e a mostrare come tali concetti abbiano trovato con il passare degli anni una adeguata controparte nell evoluzione dell hardware corrispondente. Motore, ragione e guida di ogni fase del suddetto sviluppo è stato sempre una sorta di circolo virtuoso auto-alimentante che ha come obbiettivo finale sia l aumento del realismo nella renderizzazione della scena tridimensionale, sia un interazione piu confortevole, in termini di FPS, tra l utente con ciò che viene mostrato sullo schermo. Il raggiungimento di un adeguato grado di performance in una tecnica o in un effetto grafico rappresenta solo l anticamera del bisogno spasmodico di portare la simulazione verso il livello di realismo successivo. Ogni singolo frame per second guadagnato si trasforma immediatamente in un prezioso capitale da spendere sia in nuove tecniche di visualizzazione sia in un affinamento complessivo della simulazione stessa. I produttori di hardware grafico hanno tentano di sopperire a questa continua richiesta di aumento delle prestazioni con l immissione sul mercato di nuovi device con una capacità computazionale enormemente superiore a quel- 38

42 CAPITOLO 2. GPGPU 39 li che li hanno direttamente preceduti. Parallelamente l introduzione di un livello programmativo all interno della pipeline di rendering e la definizione di linguaggi di alto livello per lo sviluppo di nuovi shader, ha permesso di dominare più facilmente tale potenza di calcolo e di estenderne i confini di applicazione. Questo ha portato negli ultimissimi anni ad una nuova linea di sviluppo: il General Purpose computation on GPU. Usare, cioè la potenza di calcolo insita nelle moderne schede grafiche per finalità completamente diverse da quelle per cui sono state originariamente progettate. Simulazioni fisiche, numeriche e chimiche, che sottostiano a determinate condizioni, possono avvantaggiarsi in maniera significativa delle prestazioni offerte dalle moderne GPU. Presentare le motivazioni che sono alla base di questa nuova offerta tecnologica e introdurne i concetti chiave sarà compito focale di questo capitolo. 2.1 CPU vs GPU: qualche numero Sebbene vanti pioneristici quanto dimenticati tentativi nei tardi anni settanta [FHWZ04] il calcolo general-purpose sulla GPU è disciplina estremamente recente che deve la sua origine al fascino indiscutibile esercitato dai valori di performance dichiarati dai produttori di hardware grafico. Nel grafico sono riportati il numero di moltiplicazioni al secondo su float a 32 bit ottenibili con varie versioni di fragment processor e il corrispondente valore raggiungibile da un Pentium 4 a 3.0 GHz. Come si può notare il confronto tra CPU e GPU è impari non solo in termini di prestazioni assolute ma, soprattutto, anche in termini di incremento medio delle prestazioni stesse. Tra il Pentium 3 e il Pentium 4 si è passati, in quarantotto mesi, da 4 a 6 gigaflop(pari ad un incremento annuale di 0.5 gigaflop), mentre in soli

43 CAPITOLO 2. GPGPU 40 sei mesi sulle schede grafiche si e partiti dagli 8 gigaflop fino a giungere ai 20 (con incremento annuale di 24 gigaflop). Tale gap è, molto probabilmente, destinato ad allargarsi in futuro. La NVidia ha annunciato che il suo attuale modello di punta, la Geforce 7800, è capace di raggiungere la ragguardevole cifra di 165 miliardi di moltiplicazioni al secondo! Anche dal punto di vista dell accesso sequenziale alla memoria l hardware grafico presenta valori sorprendenti; mentre la banda di trasferimento 1 della memoria principale è di circa 6 Gbyte al secondo in un unità di tempo la memoria locale alla GPU riesce a trasferire 17 miliardi di byte. Vedremo, comunque, in seguito che l accesso in memoria rappresenta uno dei limiti maggiori al pieno utilizzo della scheda grafica come supporto per il calcolo general purpose. A questa potenza computazionale si aggiunge anche una forte attrattiva dal punto di vista economico: il costo di una scheda grafica appena immessa sul mercato si aggira sui euro; ed il prezzo scende rapidamente nel giro di pochi mesi con l emergere di nuove soluzioni hardware. Dovrebbe essere già chiaro come questa enorme intensità di calcolo unita ad una buona banda di trasferimento dei dati abbia rappresentato un attrattiva stimolante per tentare di estendere verso nuovi orizzonti l usuale dominio di computazione della GPU. 2.2 Linee guida nello sviluppo hardware Come sottolineato nel paragrafo precedente esistono notevoli differenze negli indici di performance raggiungibili da una scheda grafica rispetto a quelli 1 Indice della quantità di dati trasferiti nell unità di tempo, si misura in byte al secondo. In letteratura è spesso riferita con il termine inglese di bandwidth.

44 CAPITOLO 2. GPGPU 41 riscontrati dall usuale unità centrale di elaborazione; questo nonostante la tecnologia dei semiconduttori avanzi con pari grado di sviluppo su entrambe le piattaforme. Tale disparità ha origini più profonde che risiedono nelle differenti finalità per cui la CPU e la GPU sono disegnate e dalle scelte architetturali da queste conseguenti. Per meglio comprendere le ragioni che sottostanno a tali divergenze di prestazione si ritiene necessario fare un breve excursus sui trend tecnologici che hanno dominato lo sviluppo hardware degli ultimi decenni e sulle problematiche derivatene Very Large Scale Integration Gli attuali processori, sia quelli dedicati alla grafica e sia le usuali CPU, sono composti da milioni di minuscole componenti interconnesse tra loro: i transistor. Nel 1965 Gordon Moore, analizzando i trend tecnologici che guidavano l avanzamento hardware del decennio, profetizzò che il numero di componenti integrabili per unità di spazio sarebbe aumentato del 100% ogni anno 2 [Moo65]. Tale tesi si è dimostrata valida per i successivi quaranta anni tanto da essere divenuta universalmente nota come legge di Moore. Non solo il numero di transistor aumenta constantemente ogni anno ma, con un percorso altrettanto virtuoso, diminuiscono le dimensioni del transistor stesso. Componenti più piccole implicano un tempo di attraversamento da parte del segnale elettrico minore, con conseguente incremento della velocità totale del chip. L aumento concorrente del numero di componenti integrabili e della loro velocità fa si che, dal punto di vista teorico, la capacità di calcolo (capability ) di un processore sia soggetta ad un incremento annuale del 71% [Owe05]. 2 In realtà nel 1975 Moore, tenendo conto della crescente complessità dei chip, riassestò il tasso di sviluppo al 50% su base annua.

45 CAPITOLO 2. GPGPU Banda di trasferimento e latenza Sebbene ogni tre anni raddoppino di dimensione [Owe05], le memorie dinamiche (DRAM) presentano dal punto di vista della banda e della latenza (misura della quantità di tempo che intercorre tra una richiesta di un dato e l effettiva disponibilità dello stesso) un incremento annuale di performance molto più limitato rispetto alla capability del processore, il che presenta un vincolo fortissimo al pieno sfruttamento delle effettiva quantità di calcolo disponibile. L accesso in memoria e, soprattutto, i tempi di comunicazione divengono così, sempre più, il collo di bottiglia dell intero sistema. Ciò constringe i designer delle architetture hardware ad implementare soluzioni che permettano un maggiore grado di tolleranza sugli effetti negativi della latenza stessa; principalmente facendo in modo di svolgere calcolo utile mentre si attende la disponibilità di un dato dalla memoria. 2.3 Indirizzi tecnologici per massimizzare la computazione In questa sezione si tenteranno di definire le caratteristiche che rendono un architettura computational intensive, in modo da poter meglio comprendere perchè dal punto di vista della nuda potenza di calcolo la GPU sovrasti le usuali unità centrali di elaborazione. Privilegiare i transistor datapath Come visto nella sezione la VLSI permette di integrare su un singolo chip un numero davvero ragguardevole di transistor; tuttavia è, bene notare, come

46 CAPITOLO 2. GPGPU 43 le finalità cui siano destinati tali transistor non siano uniche e che anzi sia possibile riconoscere tre classi principali di componenti [Owe05]: - i transistor effetivamente votati alla computazione vera e propria. In letteratura sono conosciuti con il termine di datapath. - i transistor deputati al controllo dell avanzamento della computazione (control). - i transistor che hanno funzione di immagazzinare i dati (storage). Dovrebbe essere ovvio che se il fine sia massimizzare la quantità di calcolo del processore, il chip dovrà contenere un numero il più elevato possibile di componenti datapath. Avvantaggiarsi del parallelismo Eseguire più computazioni in parallelo, ovviamente, non può che incrementare la quantità di calcolo totale effettuata dal processore. In ultima istanza si riconoscono principalmente tre tipi differenti di parellelismo: - task parallelism; permettere, cioè, di compiere in uno stesso istante operazioni differenti su dati diversi. La pipeline ne è l esempio più noto. - data parallelism; eseguire contemporaneamente la stessa operazione su più di un dato di input. L architettura farm, in cui vengono replicate le risorse di calcolo, è un espressione di questo tipo di parallelismo. - instruction parallelism; all interno dell elaborazione di un singolo dato si cerca di spezzare la computazione in sotto-operazioni che vengono eseguite concorrentemente.

47 CAPITOLO 2. GPGPU 44 Sarà presto evidente come la GPU riesca in maniera efficace ad avvantaggiarsi di tutte queste forme di parallelismo, proprio per l intrinseca natura del problema per cui è stata originariamente progettata. Comunicazioni efficienti Come già visto in precedenza le comunicazioni rappresentano i vincoli più forti alla piena espressione della potenza di calcolo insita negli attuali chip. Un processore, che aspiri a raggiungere un alto livello di performance, non può far a meno di minimizzare il più possibile le comunicazioni al di fuori del chip in cui risiede il processore stesso (off-chip comunications). Negli anni uno dei metodi più efficaci per aggirare il problema si è dimostrato il caching dei dati; tentare, cioè, di mantenere in una struttura di memoria gerarchica prossima al processore, i dati caricati dalla memoria centrale, in modo di facilitarne e velocizzarne il riutilizzo. L uso del caching, per certi versi, collide con l obbiettivo di massimizzare il numero di transistor dedicati al calcolo effettivo (datapath), in quanto, per attenuare l effetto degradante delle comunicazioni, si occupa una porzione di superficie del chip stesso con elementi di memoria. Un altra tecnica che si è dimostrata utile nel minimizzare il costo delle comunicazioni off-chip è la compressione dei dati. Solo una rappresentazione compressa del dato viene ricevuta e spedita dal processore. Ciò implica che, per attutire il degrado introdotto dallo scambio di informazioni tra un unità di elaborazione e la memoria, si sacrifichi sia spazio sul chip per l hardware dedicato alla compressione e alla decompressione, sia tempo per attuare effettivamente tali operazioni di manipolazione sui dati.

48 CAPITOLO 2. GPGPU Considerazioni sulle scelte architettoniche della CPU L unità centrale di elaborazione, proprio per il suo essere programmaticamente general purpose, è progettata con lo scopo di soddisfare le richieste più diverse. In generale, un applicazione che deve essere eseguita da una CPU può avere una natura prettamente sequenziale senza esporre alcun grado di parallelismo, oppure presentare la necessità di un monitoraggio più stringente sul flusso dell esecuzione. Con la conseguenza di aumentare la porzione di chip destinata ai transistor di controllo a scapito di quelli di datapath. Se ne deduce, quindi, che due delle linee guida che abbiamo precedentemente enunciato per la massimizzazione del calcolo, siano rimaste sostanzialmente esterne allo sviluppo tecnologico delle moderne CPU. Cosa diversa è avvenuta, invece, per quanto riguarda la minimizzazione del costo delle comunicazioni. Proprio per la sua natura sequenziale la CPU presenta la necessità di riferirsi continuamente a delle locazioni di memoria. Si consideri il frammento di codice seguente: a = b + c; d = a; Si consideri la figura 2.1 in cui, in una architettura che espone task parallelism, le operazioni di somma e di successivo assegnamento siano eseguite su due unità distinte di pipeline. Figura 2.1: Esempio di località del risultato di una computazione in pipeline

49 CAPITOLO 2. GPGPU 46 Il valore di a uscente dallo stadio di somma viene immediatamente dato in pasto al livello successivo ed il valore della variabile vive nel collegamento fisico esistente tra le due unità, favorendone implicitamente la località del dato. È evidente, invece, come nell impostazione sequenziale della CPU la variabile a abbia bisogno di essere acceduta due volte durante l esecuzione del programma, ed è altrettanto evidente, quindi, che la presenza di una cache, che mantenga la variabile stessa locale al processore, velocizzerebbe in maniera significativa il processo computazionale. Lo sviluppo tecnologico della CPU ha inseguito, negli anni, come obbiettivo prioritario la minimizzazione del tempo di latenza, non sorprende quindi che, al contrario di un processore grafico, una grande fetta della superficie del chip, su cui l unità centrale risiede, sia devoluta ad ospitare una cache gerarchica fortemente strutturata. 2.5 La GPU come stream processor I limiti maggiori in cui incorre la CPU per potersi affermare come architettura computational intensive sono dovuti principalmente, come appena visto, al moderato grado di parallelismo che le è possibile esporre. Limite che invece non affligge tutta una classe di unità di elaborazione operanti su flussi di elementi dello stesso tipo: gli stream processor. Ad ogni singolo elemento facente parte del flusso di input viene applicata una funzione descritta da una unità computazionale a cui, nel gergo degli stream processor, ci si riferisce con il termine di kernel. Tipicamente un processore operante su stream è composto da una serie di kernel posti in successione tra loro. L intera pipeline di rendering non è altro che una particolare istanza di un stream processor che ha come flusso di input una serie di vertici tridimensionali.

50 CAPITOLO 2. GPGPU 47 La pipeline grafica per la sua stessa natura espone intrinsecamente task parallelism e, se si impone che il comportamento di ogni dato del flusso è indipendente da qualsiasi altro elemento del flusso stesso, è possibile avvantaggiarsi facilmente del data parallelism replicando le risorse di calcolo. Sia la ATI X800 che l Nvidia 6800 presentano sei vertex processor e ben sedici fragment shader che operano in concorrenza tra loro. È importante notare che l indipendenza tra vertici o frammenti diversi è in realtà meno stringente di quanto posso apparire di primo acchito. In un dato istante t quel che si chiede è che il comportamento di un singolo elemento facente parte del flusso di input non sia influenzato dalle caratteristiche presentate da un altro elemento allo stesso tempo t. Nulla impone di calcolare il colore di frammento ad un istante t riferendosi al colore che un suo vicino aveva al tempo t - 1. Se ciò non fosse possibile l utilizzo della GPU come processore general purpose sarebbe estremamente limitato e poco interessante. Operando tipicamente su dati vettoriali (posizione, colore, coordinate di texture etc... ) molte delle istruzioni interpretabili dai processori di vertex e fragment shader possono essere scomposte in sotto computazioni che avvengono in parallelo sulle singole componenti del vettore. Si prenda ad esempio il prodotto scalare tra due elementi vettoriali tetradimensionali: dot(a, b) = a i b i (2.1) Tale operazione può essere tradotta immediatamente come nello pseudocodice seguente: float dot = 0.0; for(i = 0; i < 4; ++i) dot = a[i]* b[i] + dot;

51 CAPITOLO 2. GPGPU 48 Ciò ovviamente significa che il tempo totale impiegato dalla operazione di prodotto scalare è dato dal tempo delle quattro moltiplicazioni sommato a quello dovuto alle quattro addizioni. Nel caso invece di funzionalità operanti per componenti indipendenti si potrebbe immaginare di avere una funzione parallel product che prenda in input due vettori tetradimensionali e restituisca in output un nuovo vettore contenente il prodotto componente per componente dei due forniti come parametro. Tale funzione opererà in parallelo occupando il tempo di una singola moltiplicazione. Il nuovo codice generato quindi potrebbe avere l aspetto seguente: vec4 prod = parallel_product(a,b); float dot=0; for(i = 0;i<4; ++i) dot = prod[i] + dot; Tale versione avrà il costo di un unica moltiplicazione seguita da quattro addizioni 3. Operazioni come quella di prodotto scalare appena presentata rendono possibile lo sfruttamento intensivo dell instruction parallelism, favorendo un aumento significativo nella performance dell applicazione. L architettura a stream processor delle moderne GPU non aumenta solo il grado di parallelismo esposto, ma come già introdotto ha un effetto benefico anche per quanto riguarda la località dei valori che le varie unità in pipeline si scambiano. Si consideri la figura 2.1 in cui si nota come ci sia uno scambio di informazioni tra le unità successive di una pipeline, senza che questo implichi il costo di accessi in memoria. Vedremo in seguito, comunque, come la mancanza di una cache effettiva all interno del vertex e del fragment 3 È bene notare come tale il dot-product parallelo fosse già presente nel Pentium II e non sia una esclusiva delle attuali schede grafiche.

52 CAPITOLO 2. GPGPU 49 shader possa rappresentare un limite importante sulla classe di problemi che possono essere trattati efficacemente dal calcolo general purpose sulla GPU. Infine è da considerare come la stessa architettura pipeline rappresenti una sorta di (minimo) ammortizzatore rispetto alla latenza, in quanto permette comunque di compiere una certa quantità di calcolo utile nel caso uno stadio sia momentaneamente in attesa di un dato dalla memoria locale alla scheda grafica. 2.6 Meccanismi hardware per il controllo sul flusso d esecuzione Prima di addentrarci nei concetti propri del GPGPU si ritiene necessario presentare le tecniche hardware che hanno governato il flusso di esecuzione nelle ultime generazioni di schede grafiche e il costo imposto dalla presenza di cicli e codice condizionale al pieno sfruttamento della performance offerta dalle moderne GPU. I tre principali meccanismi di controllo del flusso saranno elencati di seguito in ordine cronologico di introduzione: - condition codes, permette l esecuzione di istruzioni condizionali di tipo if-then-else e di cicli for e while le cui guardie booleane dipendano da caratteristiche comuni 4 ad ogni elemento dello stream. Nel condition codes non esiste vero e proprio branching; la GPU esegue i comandi presenti su entrambi i lati di un if aggiornandone la memoria, però, solo se l istruzione appartenga effettivamente al ramo definito dalla condizione booleana. Ovviamente il condition codes (o predication codes) 4 In termini programmativi ciò implica che il valore booleno debba o essere una costante il cui valore sia noto staticamente oppure debba essere una variabile uniform.

53 CAPITOLO 2. GPGPU 50 è un tipo di controllo sul flusso di esecuzione particolarmente costoso ma per molte GPU di non ultimissima generazione 5 è l unica forma di branching supportato. - Single Instruction Multiple Data branching (SIMD), permette di eseguire istruzioni condizionali e cicli anche su attributi specifici di un singolo vertice o frammento. In una architettura SIMD tutti i vertex processor o i fragment processor presenti all interno della scheda grafica devono eseguire nello stesso istante la stessa istruzione. Se il valore della guardia booleana è identico per tutti i processori attivi allora ci si limita a valutare, nel comando condizionale, le sole istruzioni facenti parte del ramo corrispondente. Nel caso invece che i valori differiscano si attua una politica simile a quella del condition codes. È ovvio che per non incorrere in un decadimento della performance sarebbe necessario, quando possibile, evitare di incorrere in cicli divergenti. Architetture SIMD sono presenti nei fragment shader delle ultimissime generazioni di hardware grafico come la GeForce 6800 a la ATI X Multiple Instruction Multiple Data branching (MIMD), è l approccio più simile all usuale controllo del flusso di esecuzione a cui si è abituati sulla CPU. Espone lo stesso set di istruzioni del SIMD, permettendo, però, ad ogni singolo elemento dello stream di seguire un proprio path computazionale indipendente. Non presenta costi aggiuntivi oltre quelli della valutazione dell istruzione condizionale. Il MIMD è una introduzione estremamente recente all interno delle schede grafiche e finora è implementato unicamente nel vertex shader della GeForce Ancora la GeForceFX 5900 ( ) presentava esclusivamente il condition codes come sola soluzione per governare il flusso di un applicazione.

54 CAPITOLO 2. GPGPU 51 In figura 2.2 sono presentati i costi della valutazione, in termini di cicli di clock della scheda grafica, di un set di istruzioni per il branching, il looping e l invocazione di funzioni presenti nel fragment shader di una GPU attuale. Istruzione Numero cicli if-then 4 if-then-else 6 call 2 return 2 loop 4 Figura 2.2: Tabella dei costi delle istruzioni condizionali e di salto 2.7 GPGPU: Un esempio concreto In questo paragrafo si rivisiterà molto brevemente la pipeline grafica accennando a quali siano quelle caratteristiche che acquistano un ottica diversa in una prospettiva orientata al calcolo general purpose sulla GPU. Per rendere la trattazione meno astratta si presenterà un piccolo esempio di computazione sulla scheda grafica, in cui verranno nascoste, per il momento, le difficoltà tecniche in cui si incorre quando si ha a che fare con il calcolo general purpose su un device grafico. Per una esposizione più completa di tale problematiche si rimanda al capitolo quattro. In modo da aumentare la chiarezza espositiva, ci si riserva di presentare i vari stadi facenti parti della pipeline in un ordine che favorisca la linearità logica nell introduzione dei concetti chiave del GPGPU. Si supponga di voler sommare elemento ad elemento due array distinti v1

55 CAPITOLO 2. GPGPU 52 e v2 entrambi di n numeri e di memorizzarne i risultati su di un terzo array, anch esso di cardinalità n. Figura 2.3: Somma cella a cella di due array tramite GPU Struttura della memoria: Texture Nel calcolo general purpose sulla GPU le texture non sono più solo un elemento per aumentare la resa visiva di una mesh tridimensionale ma assumono il ruolo ben più importante di strumento primario per la memorizzazione delle informazioni. Le texture fondamentalmente non sono altro che array bidimensionali 6 di colori rgba 7 ; nelle ultimissime versioni di schede grafiche ciascuna componente è in grado di memorizzare un float a 32 bit, con il risultato che in un 6 In realtà sono anche unidimensionali e tridimensionali ma le schede grafiche sono particolarmente ottimizzate per la gestione di texture 2D. 7 Acronimo di red green blue e alpha; è uno degli standard più noti in computer graphics per la determinazione di un colore tramite la percentuale di saturazione delle corrispettive componenti. Qui sta principalmente ad indicare il fatto che un elemento di texture è composto da un vettore tetra-dimensionale

56 CAPITOLO 2. GPGPU 53 singolo elemento di texture sia possibile mantenere 128 bit di informazioni. Si discuteranno in seguito tutti gli aspetti e le problematiche che possono sorgere con la gestione delle texture come elemento di memoria, per ora ci sarà sufficiente immaginare che in due texture t1 e t2, entrambe di dimensioni n x 1, siano stati caricati, dall applicazione principale scritta in C++, i valori dei due array corrispondenti. Figura 2.4: Un texel di una texture può contenere quattro floating-point single precision, per un totale 128 bit per texel Fragment Shader Il centro computazionale principale di un pacchetto applicativo che si avvalga della GPU come strumento di calcolo è il fragment shader. Questo almeno per due buoni motivi: - In una GPU attuale ci sono più fragment processor che vertex processor. Come abbiamo visto in precedenza nella ATI X800 e nella GeForce 6800 il rapporto è quasi di tre fragment shader per ogni vertex shader. - I valori elaborati da un fragment shader sono immediatamente pronti per essere memorizzati in una texture mentre, nel caso si usasse un vertex processor, questi dovrebbero passare attraverso il rasterizzatore e il fragment processor, diminuendo il potere di controllo che l utente ha sulla coerenza dei dati di output.

57 CAPITOLO 2. GPGPU 54 Un fragment program per eseguire la somma elemento per elemento di due array potrebbe assomigliare a quello riportato in figura 2.5. uniform sampler2d t1; uniform sampler2d t2; varying vec2 texcoord; void main() { gl FragColor=texture2D(t1,texcoord)+texture2D(t2,texcoord); } Figura 2.5: Fragment program che esegue la somma dei due vettori Rasterizzatore È evidente, dalla struttura del codice precedente, che si abbia la necessità di far eseguire il fragment program una volta per ogni singolo elemento dell array n-dimensionale. Ciò implica, in altre parole, il bisogno di definire uno stream composto da n frammenti distinti. Nel calcolo general purpose sulla GPU questo si ottiene disegnando, nell applicazione principale, un rettangolo di dimensioni n x 1. Come si è visto nel primo capitolo il compito fondamentale del rasterizzatore è quello di identificare i singoli frammenti componenti una primitiva grafica; nel caso in esame 8 una volta definiti i quattro vertici che delimitano il quadrato lo stadio di rastering provvederà a generere gli n elementi che an- 8 Si ritiene necessario risottolineare come per favorire un approccio per gradi con le insidie insite nel GPGPU si è preferito nascondere molti ed importantissimi dettagli tecnici ed implementativi.

58 CAPITOLO 2. GPGPU 55 glbegin(gl_quads); gltexcoord2f(0.0,0.0); glvertex2f(0.0,0.0); gltexcoord2f(1.0,0.0); glvertex2f(n,0.0); gltexcoord2f(1.0,1.0); glvertex2f(n,1.0); gltexcoord2f(0.0,1.0); glvertex2f(0.0,1.0); glend(); Figura 2.6: Costruzione di un quad per attivazione frammenti e definizione delle coordinate di texture. dranno a formare lo stream di input del fragment processor. Tali frammenti, concettualmente, andranno a rappresentare le singole celle di un array. Si noti come nel codice di figura 2.6 siano state definite anche le coordinate di texture agli estremi del rettangolo; questo per permettere ad ogni frammento di poter accedere alla porzione di texture contente le informazioni relative al frammento stesso. Nell esempio precedente tali informazioni sono rappresentate dai singoli valori dei due array. La corretta interpolazione delle coordinate di texture definite ai vertici del rettangolo, come abbiamo visto nel capitolo precedente, rientra nel novero dei compiti classici devoluti all unità di rastering. Render to texture Fine ultimo del processo di rendering è, come già introdotto, passare da una descrizione tridimensionale di una scena ad un immagine bidimensionale del-

59 CAPITOLO 2. GPGPU 56 Figura 2.7: Ogni frammento attivato dal quad fa riferimento ad una cella dell array la scena stessa facilmente rappresentabile sullo schermo di un pc. È normale quindi che se non si adottasse alcun accorgimento il risultato nella nostra computazione, invece di essere memorizzato su di una texture, sarebbe visualizzato sul monitor del computer, sottoforma di un rettangolo formato da pixel di colore diverso. Tali colori non sarebbero casuali ma rappresenterebbero, ovviamente, il risultato della somma dei due array. È preferibile, quantomeno per scopi di debugging, che l output di una computazione sia mantenuto in una texture. Per far questo si usa una modalità denominata render to texture, tramite la quale si rende noto alla pipeline grafica di memorizzare i pixel componenti l immagine non più sul frame buffer (e quindi sullo schermo) ma su di una texture usata come target del processo di rendering. Vertex Shader Come abbiamo visto in precedenza il compito principale del vertex shader, nella sua accezione canonica, è quello di trasformare i vertici di un oggetto tridimensionale in coordinate 2D di schermo e di provvedere al calcolo dell illuminazione. Nel nostro esempio la computazione sull influsso delle fonti

60 CAPITOLO 2. GPGPU 57 Figura 2.8: È sufficiente definire le coordinate di texture ai quattro angoli del quad per poter permettere ad un frammento di accedere alla sua porzione di texture. Il rasterizzatore, interpolando, genera i valori corretti per ogni singolo fragment. luminose non ha alcun senso. Il nostro vertex program dovrà semplicemente trasformare quattro vertici del nostro rettangolo(che peraltro sono già bidimensionali... ) in coordinate di schermo e passare al rasterizzatore (e quindi al fragment program) le coordinate di texture da interpolare.

61 CAPITOLO 2. GPGPU 58 varying vec2 texcoord; void main() { texcoord = gl MultiTexCoord0; gl Position = ftransform(); } Figura 2.9: Vertex program minimale per la somma di due array Gather e Scatter È importante notare come i frammenti (celle dell array) a cui si applica la computazione definita dal fragment processor siano quelli del vettore che dovrà contenere il risultato del calcolo. Questo perchè, come vedremo meglio in seguito, mentre la lettura da una texture di un valore non direttamente riferibile al frammento in esame è una operazione relativamente semplice (gather), la scrittura (scatter) dello stesso è praticamente impossibile. GLSL prevede come unica modalità per la memorizzazione di un dato l assegnamento di tale valore alla variabile gl FragColor. Tale variabile indica, in uno shader classico 9 il colore che il frammento in esame andrà ad assumere sullo schermo o sulla texture usata come target del rendering. Nel caso invece di uno shader GPGPU il dato contenuto nella gl FragColor non verrà più interpretato come un colore ma come il risultato della computazione che ha per soggetto il frammento in esame. Lo scatter rappresenta uno dei maggiori limiti sulla classe di algoritmi implementabili sulla scheda grafica. Uno dei principali risultati di questo 9 Uno shader che non abbia ambizioni di general purpose computation on GPU ma solo di pura visualizzazione di una scena.

62 CAPITOLO 2. GPGPU 59 lavoro sarà mostrare un metodo per aggirare il problema dello scattering nelle mesh tetraedrali a connettività variabile dinamicamente, trasformando lo scatter stesso in una serie di più semplici operazioni di gathering. 2.8 Limiti e difficoltà del GPGPU La pura potenza di calcolo, pur essendo un buon viatico verso un sostanziale incremento delle prestazioni, da sola non garantisce un aumento effettivo della performance di una applicazione. Come già visto più volte in questo capitolo, le comunicazioni e, in misura minore, lo stesso accesso in memoria, rappresentano in molti casi vincoli che limitano pesantemente il pieno sfruttamento delle risorse di calcolo del moderno hardware. La GPU, sebbene presenti un fascino oggettivo derivatole principalmente dal numero enorme di operazioni al secondo che riesce a sostenere, non può rappresentare la panacea di tutti i mali. Esiste una amplissima classe di problemi che non può ricevere, stante l architettura attuale dei device grafici, alcun beneficio dalla potenza di calcolo insita negli stream processor. A titolo di esempio si consideri il tentativo di implementare in GPU la moltiplicazione tra matrici dense N x N, con N sufficientemente grande. Sebbene primo acchito tale problema sembrasse un candidato naturale a beneficiare delle caratteristiche computazionali offerte dalle schede grafiche di ultima generazione, l esperimento si è, invece, dimostrato fallimentare [FSH04]. I vari livelli di cache della CPU, infatti, permettono un riutilizzo dei dati tale da colmare e ribaltare l enorme vantaggio che la GPU offre dal punto di vista del calcolo puro 10. Una buona misura per comprendere a priori se una computazione general 10 È bene notare come invece nella moltiplicazione delle matrici sparse il vantaggio di performance della GPU sulla CPU è sostanziale.

63 CAPITOLO 2. GPGPU 60 purpose possa ricevere un effetivo beneficio in termini di performance da una implementazione sulla GPU è data dalla formula seguente: ia = op/ wt (2.2) Nell equazione si stima l intensità aritmetica (ia) come rapporto tra numero di operazioni matematiche effettuate (op) ed il numero di parole di memoria trasferite (wt). Ovviamente tanto più alto sarà tale rapporto tanto più grande sarà il beneficio, in termini di performance, ricevuto da una trasizione del problema sulla GPU. È doveroso, infine, precisare che la tecnologia che si tenta di usare è molto giovane e presenta, quindi, forti limiti sia strutturali sia di stabilità. Sviluppare una applicazione, anche banale, che sfrutti la capacità di calcolo della GPU può essere una operazione lunga e complessa. Una parte non irrilevante del tempo speso per lo svolgimento del lavoro in esame è stato devoluto ad aggirare i problemi introdotti da bachi nei driver della scheda grafica. L appendice B ne è una testimonianza. La cosa non deve nè sorprendere nè scoraggiare ed anzi è da considerarsi assolutamente fisiologica visto che la funzione originaria per cui l hardware grafico è stato disegnato è completamente estranea al calcolo general purpose. In molti casi i vantaggi promessi dall uso della GPU valgono il rischio di una piccola battaglia con il device grafico.

64 Capitolo 3 Elementi di fisica e di integrazione numerica Ogni modello per una simulazione del comportamento fisico di un oggetto si basa sulla seconda legge del moto di Newton: mẍ + γẋ + Erg(x) u = f (3.1) dove x indica la posizione del punto di massa m e γ il damping factor, ovvero la resistenza al movimento dovuta all attrito. Il termine Erg(x) u indica la derivata dell energia potenziale rispetto alla posizione del punto, infine f è la forza esterna esercitata sulla massa. Se, ad esempio, il punto fosse legato all origine del sistema di coordinate da una molla di coefficiente k e si stesse muovendo lungo l asse x, allora E(x) = x 0 kudu = 1 2 ku2 e quindi Erg(x) u = kx. Si supponga che un oggetto sia rappresentato da un numero di punti x 0,..., x n di massa m 0,..., m n, distribuiti in uno spazio continuo. Se si riuscisse a trovare l equazione dell energia associata alla posizione di ciascuno di questi punti si potrebbe predire e quindi calcolare il comportamento dell oggetto risolvendo le equazioni differenziali risultanti. 61

65 CAPITOLO 3. ELEMENTI DI FISICA E DI INTEGRAZIONE NUMERICA62 Modelli discreti e modelli continui Anche se si fosse in grado di descrivere il comportamento di questo oggetto per ogni punto attraverso l equazione 3.1, sarebbe necessario stabilire quale sia l insieme di punti scelto in modo tale da descrivere il comportamento del modello in maniera adeguata. Le strategie di rappresentazione di un modello possono essere raggruppate in due grandi classi: I modelli discreti ed i modelli continui. Nei modelli discreti la rappresentazione del modello viene ridotta ad un sottoinsieme dei suoi punti tali da caratterizzare in modo più realistico possibile le proprietà meccaniche dell oggetto. La complessità fisica dell oggetto richiederà una distribuzione di punti adeguata: se ad esempio l oggetto da rappresentare avesse la forma di una barra di gomma, il sistema potrebbe essere formato da un insieme di punti uniformemente distribuiti lungo l estensione della barra stessa; ognuno di essi sarebbe legato agli adiacenti tramite una molla. Più complesso è l elemento da descrivere e più complessa sarà la definizione dell energia. I modelli continui si basano invece su una rappresentazione continua dell oggetto formata da infiniti punti distribuiti nello spazio del modello. In questo caso l equazione del moto dovrà essere riscritta come : µ(u)ẍ + λ(u)ẋ + Erg(x) u dove x = x(u, t) è la posizione del punto u al tempo t. = f(x) (3.2) Siccome questa equazione è risolvibile per via analitica solo in casi ideali, il problema verrà discretizzato attraverso l analisi ad elementi finiti. 3.1 Mass spring I modelli discreti rappresentano il comportamento di un materiale attraverso un insieme di parti meccaniche elementari e un insieme di relazioni tra essi. In questo tipo si modelli i più utilizzati sono quelli che si basano su una

66 CAPITOLO 3. ELEMENTI DI FISICA E DI INTEGRAZIONE NUMERICA63 struttura di tipo particle system; la loro popolarità è dovuta soprattutto alla semplicità di realizzazione oltre che alla loro versatilità. Una particolare istanza del Particle system per la descrizione degli oggetti deformabili è il modello Mass spring. In esso le particle che costituiscono il sistema sono interconnesse tra di loro tramite molle. Le connessioni tra particle possono seguire ad esempio una rete spigoli di celle tetraedrali che descrivano l intero volume dell oggetto deformabile. Il valore della forza interna agente sulla particella P i assume la forma: F i = j N(P i ) k ij ( P i P j l 0 ij) P ip j P i P j (3.3) Dove N(P i ) è l insieme dei vertici P j adiacenti a P i, k ij è il coefficiente di rigidità tra i vertici P i e P j, l 0 ij è la lunghezza iniziale della molla tra P i e P j. Il modello Mass spring costituisce nel complesso un metodo abbastanza semplice da implementare; purtroppo la resa dal punto di vista del realismo fisico è più scarsa rispetto ai metodi continui come il FEM. Nel FEM la variazione di energia interna dell oggetto viene calcolata basandosi sul volume del dominio di integrazione prescelto all interno del quale la massa è distribuita in maniera uniforme. Questo tipo di modello fisico costituisce una rappresentazione più accurata della realtà; le differenze col modello Mass Spring si notano poi in termini di realismo durante la simulazione. 3.2 Metodi di integrazione numerica I vertici che compongono un oggetto deformabile si muovono in relazione alle forze interne scaturite durante la deformazione. Utilizzando le equazioni del moto si è in grado di stabilire, conoscendo le forze interne al modello, la posizione di ciascun vertice v in un istante di tempo t. Per risolvere

67 CAPITOLO 3. ELEMENTI DI FISICA E DI INTEGRAZIONE NUMERICA64 le equazioni del moto è necessario risolvere delle equazioni differenziali di secondo grado. Ogni equazione differenziali con grado maggiore di uno può essere riscritto in un sistema di equazioni di primo ordine, il problema si riconduce quindi alla soluzione del sistema: ẋ(t) = φ(x, t) (3.4) I metodi più utilizzati per la risoluzione numerica di questi sistemi id equazioni sono i metodi di Runge Kutta Metodi di Runge Kutta L idea base è quella di esprimere x(t + h) utilizzando la serie di Taylor: x(t + h) = x(t) + hx (t) + h2 2! x (t) + h3 3! x (t) + hn d n x (3.5) n! dt n Assumendo che x(t) sia C, è possibile troncare questa serie ad un dato termine, ad esempio il secondo: x(t + h) = x(t) + hx (t) + O(h 2 ) (3.6) Si otterrà così una approssimazione di x(t + h) con un errore O(h 2 ). Sia φ la funzione che calcola x possiamo scrivere la formula precedente come: x(t + h) = x(t) + hφ(x, t) + O(h 2 ) (3.7) Questa approssimazione del metodi di Runge-Kutta al primo ordine viene chiamato anche metodo di Eulero. Il metodo di Runge-Kutta al secondo ordine viene ottenuto troncando la serie al terzo termine della serie. L approssimazione di x(t + h) genererà un errore O(h 2 ). Chiaramente maggiore sarà

68 CAPITOLO 3. ELEMENTI DI FISICA E DI INTEGRAZIONE NUMERICA65 l ordine e più accurato sarà il risultato, d altra parte il numero di valutazioni di φ(x, t) è un punto cruciale nella complessità computazionale. Questi metodi vengono detti espliciti in quanto ciò che non è conosciuto viene espresso come soluzione di un qualcosa che già è noto Metodi impliciti Diversamente dai metodi espliciti nei metodi impliciti si esprime ciò che non è conosciuto come soluzione di un sistema di coppie di equazioni. Per le equazioni differenziali questo significa esprimere il valore x(t + h) utilizzando φ(x(t + h), t + h), in particolare il passo di Eulero può essere riscritto come: x(t + h) = x(t) + hφ(x(t + h), t + h) + O(h 2 ) (3.8) dove il termine φ(x(t + h), t + h) viene approssimato attraverso la serie di Taylor troncata al primo termine: φ(x(t + h), t + h) = φ(x(t), t) + hφ (x(t), t) (3.9) Si consideri adesso che x è una variabile singola appartenente all insieme dei numeri reali, x(t) e φ(x, t) sono vettori, mentre φ(x, t) è una matrice. Sostituendo 3.9 in 3.8 e risolvendo per x(t + h): x(t + h) = x(t) + ( 1 h I φ (x(t), t)) 1 φ(x(t), t) (3.10) Questo metodo richiede la computazione di una matrice inversa (o equivalentemente risolvere un sistema lineare) ad ogni passo della simulazione. Questo rende i metodi impliciti più complessi in termini di numero di operazioni rispetto ai metodi espliciti per un singolo passo di integrazione. D altra parte sono metodi che possono essere utilizzati in contesti pratici in cui il

69 CAPITOLO 3. ELEMENTI DI FISICA E DI INTEGRAZIONE NUMERICA66 valore di h è elevato. Entrambi i metodi (espliciti ed impliciti) fanno parte della classe di metodi chiamati single step, ovvero i metodi che esprimono il valore di x(t + h) usando solamente lo stato dello stato precedente (ovvero x(t)). Al contrario i metodi multistep utilizzano una interpolazione dei precedenti n punti: x(t + h) = x(t) + hφ(β 0 x(t + h) + β 1 x(t) β n x(t (n 1)h)) (3.11) Si fa notare come questa formulazione esprima anche i metodi single step (in questo caso i = 1) sia espliciti (β 0 = 0) che impliciti (β 0 0).

70 Capitolo 4 Complessi simpliciali in GPU In questo capitolo verrà presentata una possibile implementazione di un sistema di masse-molle che sfrutti la potenza di calcolo offerta dai moderni device grafici e tenti di superarne, per quanto possibile, le limitazioni imposte dalla struttura. 4.1 Elementi e complessi simpliciali Definizione 1 Dato uno spazio euclideo R n ed un insieme di punti distinti {a 0,..., a n } appartenenti a tale spazio l insieme è detto geometricamente indipendente se le equazioni n i=0 t i = 0 n i=0 t ia i = 0 presentano come unica soluzione t 0 = t 1 =... = t n = 0. Segue immediatamente dalla definizione che un insieme formato da un unico elemento non zero è sempre geometricamente indipendente. 67

71 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 68 Definizione 1 Sia L = {a 0,..., a n } un insieme di n + 1 vertici geometricamente indipendenti. Si definisce un n simplesso σ come l insieme dei punti x tali che: x = n t i a i i=0 n t i = 1 i.0 t i 1 i=0 La definizione permette di costruire nello spazio R 3 n-simplessi per n 3. Uno 0-simplesso sarà costituito da un unico punto mentre due vertici a 0 e a 1 andranno a formare l usuale equazione del segmento di retta x = a 0 t + a 1 (1 t) in cui 0 t 1. Meno evidente è la descrizione di un simplesso di dimesione n = 2. Si consideri la definizione di un 2-simplesso σ: x = 2 t i a i (4.1) i=0 t 0 + t 1 + t 2 = 1 0 t 0, t 1, t 2 1 la prima delle equazioni può essere altresì espressa come: x = t 0 a 0 + (1 t 0 )[(t 1 /λ)a 1 + (t 2 /λ)a 2 ] dove λ = 1 t 0. L espressione tra parentesi quadrate identifica a sua volta un simplesso di dimensione uno. Dalla seconda equazione si ricava, infatti, che (t 1 + t 2 )/(1 t 0 ) = 1 e dalla condizione di restrizione del dominio di validità degli t i si ha che i t i /λ sono a loro volta compresi tra zero ed uno. Come visto poc anzi un 2-simplesso è identificato geometricamente da un segmento; ciò implica che il punto p definito dall espressione tra parentesi

72 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 69 vive all interno del segmento di retta passante tra a 1 e a 2. Inserendo tale p nell equazione 4.1 si ottiene così x = t 0 a 0 + (1 t 0 )p che, come da disegno 4.1, indica un fascio di rette che spazza un area triangolare. Figura 4.1: Costruzione geometrica del simplesso di dimensione due Costruzione del tutto analoga permette di identificare nel tetraedro il simplesso di dimensione tre. Definizione 1 Un sotto-simplesso σ costituito su un insieme di punti appartenenti a un n-simplesso σ viene detto faccia di σ stesso. Si parlerà di faccia propria nel caso in cui σ σ. Si ha così, ad esempio, che l insieme delle facce di un simplesso tetraedrale sarà composto da: - i quattro vertici che lo definiscono - i sei segmenti che uniscono i punti - le quattro facce triangolari

73 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 70 - il tetraedro stesso Definizione 1 Un complesso simpliciale Γ a d dimensioni (o d-complex) è un insieme di simplessi tali che: - per ogni simplesso σ appartenente a Γ, tutte le facce di σ appartengono a Γ. - per ogni coppia di simplessi σ, σ appartenenti a Γ, tali che σ σ, σ σ è una faccia di entrambi (σ e σ ). - d è il massimo ordine di simplessi appartenenti a Γ. In altre parole un complesso simpliciale non è altro che la composizione di singoli simplessi attaccati 1 l uno all altro lungo una delle loro facce. Le usuali mesh triangolari (già introdotte nel primo capitolo) e quelle tetraedrali rappresentano due casi notevoli di complessi simpliciali. Oggetti tridimensionali costituiti su tali mesh diveranno target privilegiato del resto del lavoro in esame. 4.2 Gestione della dinamica dei complessi simpliciali Per facilitare la comprensione delle molteplici problematiche in cui si incorre implementando un algoritmo general purpose operante su GPU, le restanti sezioni desciveranno la gestione del motot in tre dimensioni di una mesh tetraedrale a connettività variabile in maniera progressiva partendo dal caso una catena di masse e di molle che si muovono in uno spazio 2D. 1 In realtà, come è specificato dalla definizione, possono essere costruiti complessi simpliciali costituiti da simplessi staccati tra loro.

74 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Catena di masse in CPU Si ritiene utile, di rimarcare le differenze di approccio allorquando si usi la scheda grafica come unità di calcolo general purpose, presentare una tipica realizzazione C++ di una catena di masse legate l una alle altre attraverso molle estensibili. In un sistema masse-molle, come già introdotto nel capitolo tre, le masse sono enti puntiformi adimensionali descritti da una massa e da un insieme di grandezze fisiche vettoriali 2 ; tipicamente posizione, velocità ed accelerazione. class Mass { const float _mass; const Point2<float> _p; const Point2<float> _v; const Point2<float> _a; }; Figura 4.2: Massa in CPU Una molla, come da codice 3 in figura 4.3, è identificata dai due riferimenti alle masse appese ai suoi estremi, da un fattore di elasticità k e dalla lunghezza che la molla stessa presenta in posizione di riposo. È, infine, necessaria la presenza di una classe collettore MassSpringSystem contenente tutte le istanze delle masse e delle molle. Il compito principale di tale classe è quello di gestire la dinamica dell intero sistema di simulazione. 2 Nel codice di esempio si è fatto uso di una classe template Point2<T> che astrae punti e vettori bidimensionali dal tipo scalare. 3 Per favorirne la leggibilità il codice è stato epurato da tutti quegli elementi, tipo i costruttori ed operatori di accesso ai membri privati della classe, che non aggiungono nulla alla trattazione in esame.

75 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 72 class Spring { const Mass& _m1; const Mass& _m2; const float _k; const float _qlength; }; Figura 4.3: Molla in CPU class MassSpringSystem { std::vector<mass> _ms; std::vector<spring> _sp; }; public: void update(const float dt); void draw() const; Figura 4.4: La classe contenente le masse e le molle che formano il sistema La procedura di update, parametrica sul tempo intercorso dall ultima invocazione della funzione stessa, si compone sostanzialmente di due cicli. Il primo, operante su tutte le molle, ha il compito di aggiornare le accelerazioni a cui ogni singola molla sottopone le masse attaccate ai suoi estremi. Si evidenzia dalla figura 4.5 come ogni massa (tranne la prima e l ultima) sottostia alle forze di richiamo impresse da due molle differenti; in altre parole lo scopo di tale iterazione è quella di sommare tutte le forze che agiscono su una singola particella. Il secondo ciclo opera il passo di integrazione vero e proprio sull accele-

76 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 73 Figura 4.5: Ogni molla s trasmette alle masse attaccate ad essa la forza elastica calcolata. Tipicamente su ogni s, tranne agli estremi della catena, incideranno due masse razione totale che ogni massa si trova a subire; aggiornandone così prima la velocità ed in seguito la posizione. void MassesSpringsSystem::update(const float dt) for(every spring s in _sp) Point2<float> vdiff = s.m2().p() - s.m1().p() Point2<float> vforce = s.k() * vdiff.normalize() * (vdiff.abs() - s.qlength()) s.m1().a() -= vforce / s.m1().m() s.m2().a() += vforce / s.m2().m() for(every mass m in _ms) m.v() = m.v() + m.a() * dt m.p() = m.p() + m.v() * dt Figura 4.6: Codice della funzione di update del sistema di simulazione Tipicamente una simulazione fisica al computer presenta un ciclo infinito di aggiornamento degli attributi e successiva renderizzazione degli elementi oggetto della stessa. Tale simulazione avviene per istanti discreti; ad ogni iterazione un nuovo dt permetterà di tener traccia del tempo inter-

77 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 74 corso dal passo di integrazione precedente. Tanto più alta sarà la frequenza di aggiornamento della simulazione (tanto minore sarà il dt) tanto più essa risulterà accurata. Senza contare poi, come visto nel capitolo tre, che alcuni metodi di integrazione numerica presentano una soglia di divergenza molto bassa, superata la quale la stessa simulazione perde totalmente di significato. void updateanddraw() MassesSpringsSystem ms while(true) float dt = time_passed_from_last_call_to_update() ms.update(dt) ms.draw() Figura 4.7: Ciclo di aggiornamento e disegno della simulazione 4.4 Simulazioni fisiche in GPU Nella presente sezione si introdurrà la struttura principale che le scelte architetturali della GPU impongono ad una qualsiasi simulazione fisica Parametri di input di un fragment program Come visto nel capitolo due nel calcolo general purpose su scheda video le texture assumono il ruolo di dispositivo primario di memoria e il frammento diviene il tipo di dato principale su cui ha luogo la computazione. Un fragment program definisce una funzione di transizione dello stato di un frammento. Tale funzione ha come input un numero di parametri arbitrari

78 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 75 e tra questi, sicuramente, il frammento stesso 4 e, come output, restituisce una quadrupla di valori che normalmente rappresenteranno il colore rgba del fragment. Di per se il frammento che esce dalla fase di rastering contiene poche informazioni utili alla computazione che ha luogo sul fragment processor. Nel caso del calcolo general purpose in GPU esse sono fondamentalmente tre: - Il fatto di esistere 5, che come abbiamo visto precedentemente, è condizione imprescindibile per l attivazione di un fragment program; il quale infatti, si ritiene doveroso sottolineare ancora una volta, non ha luogo per ogni pixel 6 dello schermo ma solo per quei frammenti che andranno a comporre sul monitor l immagine della primitiva geometrica da visualizzare. - Una posizione in coordinate di schermo, che indica a dove il fragment shader dovrà andare a scrivere il risultato della sua computazione. - Le coordinate di texture relative al frammento. Con esse il fragment può accedere ad una texture dove potranno essere memorizzate informazioni aggiuntive sullo stato del frammento stesso e sullo stato globale del calcolo. Queste informazioni, tipicamente, andranno a costituire l insieme dei parametri di input su cui agirà il fragment program. 4 Ciò non implica che quando si debba scrivere un fragment program sia necessario passargli in input effettivamente il frammento. È il meccanismo degli shader che provvederà a fornirlo al programma come suo parametro implicito. 5 Che più che una informazione è una precondizione. 6 Sebbene, con una toponomastica fuorviante, in letteratura ci si riferisca al fragment shader talvolta anche con la locuzione pixel shader.

79 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Risultato di una computazione Si può paragonare il meccanismo degli shader come un iteratore funzionale su un insieme di elementi del medesimo tipo; elementi a cui verrà applicata la procedura definita dall utente come corpo dello shader stesso. Nel caso di un fragment program tali elementi saranno, ovviamente, i frammenti risultanti dalla operazioni di rastering. Compito dello shader sarà quello di assegnare al frammento una quadrupla di valori coerenti alla computazione che ha luogo sul fragment processor. Un fragment di per se, senza le informazioni aggiuntive derivategli dalla porzione di texture che gli è destinata, è un segnaposto, una casella postale fissa che verrà riempita dal fragment shader con il risultato della computazione che ha luogo nel fragment processor (fig. 4.8). Si ritiene necessario sottolineare come questo sia l unico aggiornamento allo stato globale del calcolo che un fragment program possa effettuare. Da questo si deduce come la prima condizione necessaria per poter implementare un algoritmo su un singolo fragment program sia che questo preveda come output un insieme di valori memorizzabili in 128 bit (rgba memorizzati in float single precision). Non solo. Nel capitolo due si faceva riferimento al problema dello scatter, sottolineando come, stante le caratteristiche tecniche delle attuali schede grafiche, sia impossibile aggiornare lo stato interno di un frammento che, in un dato istante, non sia il soggetto principale delle operazioni intraprese dal fragment program. Ad esempio, mentre lo pseudocodice 7 di figura 4.9 è di facile traduzione in uno shader equivalente operante 7 Nello pseudo-codice la funzione choose another fragment(streamoffragments, fr) restituisce un nuovo frammento contenuto in streamoffragments differente da fr. Si è preferito evitare usare funzioni, tipo fr.next(), che avrebbero potuto generare l idea, totalmente errata, che sia possibile determinare un qualsiasi ordine all interno di un flusso di frammenti. La funzione some value(fr) restituisce, dato un frammento qualsiasi, un valore intero

80 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 77 Figura 4.8: Il rasterizzatore genera un numero di frammenti neutri proporzionali alla dimensione del quad di attivazione. Su tali frammenti il fragment shader applicherà il programma definito all interno del fragment processor e scriverà il risultato nella posizione del corrispondente pixel sul color buffer in quel momento attivo in GPU (in quanto si aggiorna lo stato interno dell elemento su cui si sta ciclando), lo stesso non si può dire per il codice di figura 4.10, in cui ad un frammento che non è il soggetto dell iterazione viene assegnato un valore arbitrario Interazione tra applicazione principale e shader Seppur finora ci si sia principalmente concentrati sulle problematiche inerenti fragment e, in misura minore, vertex shader, è bene sottolineare come un dipendente da quel frammento.

81 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 78 for_each_fragment(fr in streamoffragments) fr.r = some_value(choose_another_fragment(streamoffragments, fr)) fr.g = some_value(choose_another_fragment(streamoffragments, fr)) fr.b = some_value(choose_another_fragment(streamoffragments, fr)) fr.a = some_value(choose_another_fragment(streamoffragments, fr)) Figura 4.9: Il frammento soggetto del fragment program calcola un proprio attributo tramite lo stato di un altro frammento. Nessun problema for_each_fragment(fr in streamoffragments) choose_another_fragment(streamoffragments, fr).r = some_value(fr) choose_another_fragment(streamoffragments, fr).g = some_value(fr) choose_another_fragment(streamoffragments, fr).b = some_value(fr) choose_another_fragment(streamoffragments, fr).a = some_value(fr) Figura 4.10: Un frammento diverso da quello soggetto del ciclo tenta di aggiornare il proprio stato. Impossibile pacchetto applicativo che sfrutti la potenza di calcolo dell hardware grafico per computazioni general purpose viva su due livelli programmativi distinti. Quello degli shader veri e propri e quello dell applicazione principale scritta in un tipico linguaggio di programmazione (c, c++, Java, c#,... ) che attiva, gestisce e controlla l operato di vertex e fragment program. Comprendere quando e come si passi dall uno all altro livello e il modo in cui le due parti comunichino è fondamentale per il proseguo della trattazione. Tipicamente la compilazione di un vertex o fragment program in un assembler comprensibile per il device grafico avviene nel run-time dell applicazione principale, tramite la funzione glcompileshaderarb 8. Compilare lo 8 L acronimo ARB sta per Architecture Review Board; è dal 1992 il consorzio indipen-

82 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 79 shader di per se non implica alcuna funzione di attivazione sullo shader stesso. Senza l invocazione della procedura di gluseprogramobjectarb tutte le istruzioni OpenGL sarebbero assunte come operanti sulla pipeline standard. Come abbiamo già visto per la CPU la visualizzazione di una simulazione fisica impone un ciclo continuo di aggiornamento dello stato degli elementi coinvolti nella simulazione e una successiva renderizzazione degli stessi. Concettualmente il meccanismo è valido anche per la GPU ma data la natura dell hardware grafico esso presenterà delle caratteristiche proprie. particolare sarà necessario assicurarsi che il calcolo implementato nel fragment program abbia effettivamente luogo, cioè che la fase di rastering generi uno stream di frammenti su cui il fragment processor possa operare. far questo, come già è stato introdotto, è sufficiente disegnare una primitiva geometrica, tipicamente un rettangolo, costituita da un numero di pixel 9 pari al numero degli elementi oggetto della computazione. Altresì nel capitolo due veniva sottolineato che il target del rendering non potesse essere il monitor del pc (pena la visualizzazione di un rettangolo dai colori all apparenza casuali) ma dovesse essere un device nascosto all occhio dell utente. Nella prima parte della trattazione ci si affiderà ad un pixel-buffer, che non è altro che una porzione di memoria dalle caratteristiche del tutto simili all usuale color-buffer ma che non è destinata ad essere disegnata sullo schermo. Il ciclo di aggiornamento della simulazione impone che al fragment program, destinato alla gestione del calcolo vero e proprio degli attributi fisici di un oggetto, vengano passati ad ogni iterazione lo stato degli attributi calcolati nel passo di integrazione precedente. Tipicamente tali valori sono mantenuti dente che governa l evoluzione delle specifiche di OpenGL. Tipicamente una funzione con estensione ARB è ormai prossima ad entrare nello standard dell API. 9 Si è usato il termine pixel perchè in questo caso non vi è alcuna differenza tra il numero di frammenti e quello dei pixel stessi. In Per

83 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 80 in una texture che, in un linguaggio di shader, rappresenta il corrispettivo degli usuali array. Inoltre, essendo facilmente accedibili da entrambe i livelli, le texture assumono il ruolo di canale di comunicazione privilegiato con cui applicazione principale e shader si scambiano informazioni sull avanzamento del calcolo. Non a caso sarà compito del programma c++ riempire le texture con i valori iniziali della simulazione per dar modo allo shader di compiere il primo passo di integrazione. Di per se il fragment processor, pur avendo la capacità di indirizzare la lettura di qualsiasi texel all interno di una texture, non può in alcun modo scrivere su di essa. Un fragment program non modifica il contenuto di una texture ma, come visto in precedenza, può solo scrivere nella posizione del pixel-buffer corrispondente al frammento (tramite la variabile predefinita gl FragColor) che in un dato istante sta valutando. Per ovviare a tale problema in questa prima parte il programma principale accederà al pixel-buffer 10 copiando in una texture tutti i frammenti che lo compongono. Una volta copiata su di una texture il nuovo stato degli elementi da renderizzare, all applicazione principale è sufficiente disabilitare lo shader e ritornare il target del rendering allo schermo del pc per ridisegnare gli oggetti nelle nuove posizioni. Tali posizioni, per ora, verranno lette dalla CPU direttamente dalla texture generata dal calcolo implementato sul fragmentprogram. Nella figura 4.11 è riassunto lo scheletro principale del ciclo di simulazione introdotto in questo paragrafo. 10 Tramite comando gl PixelTransfer.

84 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 81 Figura 4.11: Interazione tra applicazione principale e GPU. Si fa notare come le due parti dialoghino tramite texture passanti sul bus Tipi di dato Sebbene il frammento/texel presenti un layout prefissato, 128 bit di memoria divisi su quattro parole da 32 bit ciascuna indirizzabili singolarmente, è possibile associare al frammento più informazione, utilizzando parte dei

85 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU bit come puntatore ad un altra texture che si intende relazionare con il frammento. Ad esempio una stringa potrebbe essere costruita usando due texture. Nella prima, di dimensioni pari al numero di stringhe necessarie alla computazione, si terranno le informazioni per accedere ai caratteri, codificati ascii, memorizzati in celle consecutive della seconda texture. In particolare nel campo red si inserirà l indice base della sequenza di texel e nel campo green il numero di caratteri contenuti nella stringa 11. Tale rappresentazione è illustrata in figura Figura 4.12: Hello world! caratteri della stringa è codificata ascii in GPGPU. Nella texture rossa la sequenza di Il nodo focale sarà comprendere quanto possa essere utile una tale rappresentazione di un tipo di dato; quali operazioni saranno possibili su di esso e a quali limitazioni dovrà sottostare. 11 Potendo contenere ogni texel quattro caratteri il numero di celle occupate da una stringa sarà numero caratteri/4.

86 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Catena di masse in GPU Sebbene il modello di calcolo proposto per l unità centrale di elaborazione presenti in GPGPU delle difficoltà implementative dovute all architettura dei device grafici, per comprenderne appieno le ragioni si tenterà, comunque, di fornirne una interpretazione operante sulla GPU. Nella sezione corrente si presenteranno con maggior grado di dettaglio difficoltà, limiti e problemi che il calcolo general purpose su hardware grafico porta con se Masse e molle La classe Spring, introdotta nel paragrafo 4.2, presentando solo quattro campi è di facile traduzione in una versione operante in GPU; l unica modifica necessaria è trasformare i riferimenti alle masse appese agli estremi della molla con due indici della texture destinata a contenerle. Una volta noto al fragment-program le dimensioni di tale texture tramite le usuali operazioni di modulo e di divisione è operazione relativamente semplice, data una molla, accedere alle informazioni necessarie. Si ricorda che essendo le coordinate di texture comprese tra zero ed uno anche l indice dovrà essere di tipo float e operante sullo stesso intervallo. Si sottolinea, inoltre, come la figura non si possa neanche considerare pseudo-codice in quanto, come visto, non si sta definendo un tipo ma si sta fornendo una interpretazione dei campi di uno già esistente. Con il simbolo & si sarà soliti indicare un indice di accesso ad una texture. La struttura di Mass presenta difficoltà maggiori, in quanto ogni attributo di accelerazione, velocità e posizione è a sua volta composto da due coordinate x ed y. Per aggirare tale difficoltà ci sono due modi: - Si costruiscono due texture t1 e t2 delle stesse dimensioni. Nella prima

87 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 84 struct gpuspring { field r: &_m1 field g: &_m2 field b: _k field a: _qlength } Figura 4.13: Prima definizione di una molla in GPU seguendo lo schema di calcolo imposto dalla CPU si mantengono le informazioni di posizione ed un indice di accesso alla seconda texture, nella quella si salveranno velocità ed accelerazione. Tale indice non sarà altro che l identificativo univoco della massa stessa. - Si costruisce una sola texture di dimensioni pari al doppio delle masse. Nella prima metà si mantiene la posizione e la velocità, nella seconda l accelerazione. Per poter accedere alla due parti si fa ricorso al multitexturing, cioè alla possibilità che ha un frammento di poter contenere più di una coppia di coordinate di texture 12. Per ragioni di espandibilità dell applicativo si sceglie di seguire la seconda tecnica. È bene notare, infatti, che uno shader può accedere in un dato istante ad un numero di texture molto limitato. Tale numero, dipendente dalle caratteristiche della scheda grafica che si sta usando, si aggirà di solito tra le otto e le sedici texture. 12 Si vedrà fra poco come questo possa essere implementato.

88 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Difficoltà implementativa del modello Nel modello di calcolo implementato per la CPU la funzione di update del sistema masse-molle prevede due cicli: uno sulle molle con l aggiornamento delle accelerazioni subite dalle masse attaccate agli estremi ed uno di integrazione di velocità e posizione di ogni massa della simulazione. Sebbene, come vedremo in seguito, anche la seconda iterazione presenti alcune problematiche implementative le vere difficoltà risiedono nel primo ciclo. Implementare un algoritmo strutturalmente simile a quello della CPU sul device grafico implica la necessità di due passate di calcolo differenti: uno che abbia come soggetto le molle ed un altro che, invece, agisca sulle masse. Questo porta, come conseguenza, il dover scrivere due fragment-program distinti, che abbiano luogo l uno di seguito all altro. Si dovrà, quindi, aggiornare l ossatura dell interazione tra applicazione principale e shader per come era stata definita nel paragrafo Come si nota in figura 4.14 è necessario disegnare due quad per attivare i corrispettivi shader. Tali quad hanno grandezze diverse in quanto in una catena di masse-molle ci sono n masse e n 1 molle. Il fragment-program operante sulle molle potrebbe avere la struttura riportata in figura Nel codice si accede, tramite le coordinate di texture proprie del frammento, alla texture contenente tutte le molle. Una volta ottenuta la molla corrispondente al frammento che ha attivato il fragmentprogram con gli indici delle due masse si eseguono i due look-up in texture per ottenere i vecchi valori della accelerazione e se ne computano i nuovi. A questo punto, però sorgono i problemi. Quello che intenzionalmente si vorrebbe fare con l istruzione di gl FragColor è di andare a scrivere la somma della vecchia e della nuova accelerazione nella texture mass nelle corrispettive posizioni delle masse attaccate agli estremi della molla ma, come è stato

89 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 86 Figura 4.14: Interazione tra GPU e applicazione principale. Per simulare lo schema di calcolo della CPU sono necessari due shader di calcolo: uno operante su molle, l altro sulle masse

90 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 87 più volte ribadito, un fragment-program non può in alcun modo accedere in scrittura su di una texture ma solo sul frammento da cui è stato attivato. Modificando leggermente il fragment-program (evitando di sommare la vecchia accelerazione alla nuova e scrivendo nel frammento direttamente quest ultima), il problema si potrebbe aggirare demandandolo alla CPU. In pratica, prima di attivare lo shader operante sulle masse, l applicazione principale che, nel frattempo, ha comunque scaricato dalla memoria video i pixel contenenti i risultati della computazione del fragment-program e li ha salvati in una texture temporanea tmp text, potrebbe eseguire una fase di dispatching dei valori. In tmp text la posizione di una cella di texture indica anche la molla che ha prodotto quel risultato, ad esempio i valori memorizzati nel quinto texel si riferiranno alla quinta molla. E visto che la molla n ha agli estremi la massa n e la massa n + 1 la CPU potrebbe andare a sommare per il fragment-program i valori delle accelerazioni nella texture delle masse prima di attivare lo shader operante sulle stesse. Tale procedimento, seppur possa apparire una buona soluzione per una catena di masse, si complicherebbe parecchio non appena si passasse ad una mesh con connettività meno regolare. Una mesh, cioè, in cui non sia implicito dire, data una molla, chi siano le masse corrispondenti. Infine facendo svolgere parte del calcolo della simulazione fisica alla CPU andrebbe contro gli scopi della trattazione in esame. Nel fragment-shader di figura si è fatto uso della funzione coordtx per l accesso in texture tramite un indice di un elemento e le dimensioni della texture stessa. Si noti come in OpenGL l indirizzo di una cella non sia relativo all angolo in basso a sinistra ma si riferisca al centro della cella stessa; per questo nella funzione in esame si è dovuto far ricorso alla variabile halftex che contiene appunto le coordinate del centro di un texel. La funzione

91 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 88 uniform sampler2d spring; uniform sampler2d mass; uniform vec2 mass_tex_dim; uniform float dt; varying vec2 texcoord; vec2 coordtx(float ind, vec2 dimtext) { vec2 halftex = 1.0 / dimtext / 2.0; return vec2(mod(ind,dimtext.x) / dimtext.x + halftex.x, floor(ind / dimtext.x) / dimtext.y + halftex.y); } void main(void) { vec4 sp=texture2d(spring,texcoord); vec4 old_acc1 = texture2d(mass,coordtx(sp.r,mass_tex_dim)); vec4 old_acc2 = texture2d(mass,coordtx(sp.g,mass_tex_dim)); vec2 new_acc1 = compute_acc(sp.r,sp.b,sp.a,dt); vec2 new_acc2 = compute_acc(sp.g,sp.b,sp.a,dt); gl_fragcolor = vec4(new_acc1.x + old_acc1.x, new_acc1.y + old_acc1.y, new_acc2.x + old_acc2.x, new_acc2.y + old_acc2.y); } Figura 4.15: Fragment program dettato dallo schema di calcolo della CPU. Si vorrebbe scrivere la nuova accelerazione all interno della texture delle masse ma ciò per una molla è assolutamente impossibile

92 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 89 coordtx sarà assunta come sempre definita negli esempi di codice successivi. 4.6 Una nuova strategia di calcolo Sebbene presenti delle controindicazioni forti, come appena visto, il dispatching fornisce una base per lo sviluppo di una proposta algoritmica alternativa per la simulazione di un sistema masse-molle su GPU. Ciò che si fa nella soluzione precedente è di far pervenire ad una massa le accelerazioni fornite dalle molle che la condividono. Il che dovrebbe invitare a riflettere su cosa accadrebbe se invece di essere oggetto passivo dell azione le masse divenissero soggetto centrale della stessa, capovolgendo radicalmente la prospettiva finora seguita. Invece di due cicli distinti, uno operante su molle ed uno sulle masse, si demandano tutte le operazioni alle masse; non solo, quindi, esse saranno responsabili del passo effettivo di integrazione numerica ma anche del calcolo delle forze che le molle incidenti su di una massa impongono alla massa stessa. Come si notava precedentemente una molla n è connessa con la massa n e con quella n + 1. Rovesciando la relazione si ha che la massa n è condivisa dalla molla n 1 e dalla molla n, con l ovvia eccezione delle masse ai due estremi della catena; entrambe connesse ad un unica molla. Quindi conoscere l identità di una massa implica conoscere le molle che operano su di essa. Purtroppo, però, in uno shader la cosa non è così immediata. È stata già sottolineata più volte nei capitoli iniziali l impossibilità, per vertex e fragment shader, di definire un ordine sui rispettivi domini di applicazione. Nulla impone che il frammento in basso a destra dello schermo venga processato o per primo o per ultimo o per venticinquesimo. Diventa quindi necessario che un frammento acquisti, in un certo senso, coscienza di se; in particolare

93 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 90 che riesca a determinare quale sia la sua identità in un sistema masse molle. La soluzione più indolore per ottenere tale risultato è di aggiungere in ogni cella della texture delle masse l identificativo del frammento a cui quel texel è destinato Organizzazione di dati e texture Definire la rappresentazione della massa è impresa più ardua di quanto lo sia stato per la molla in precedenza. Si ha già bisogno di due parole di memoria per ciascuno degli attributi di posizione, accelerazione e velocità, più una parola suppletiva per definire l identità della massa stessa. Il tutto non può essere contenuto all interno di un singolo frammento o in una cella di una texture. Per ovviare a tale difficoltà, come già introdotto nel paragrafo 4.5.1, esistono due strategie; nel proseguo di questo lavoro si è scelto di organizzare la texture delle masse a strati; ogni strato sarà composto da un numero di celle pari al numero delle masse del sistema. Per poter accedere ai diversi segmenti della texture si farà ricorso al multi-texturing; allorquando si disegnerà il rettangolo di attivazione del calcolo verranno specificate, per ognuno dei quattro vertice del quad, un numero di coordinate di texture pari al numero di strati di cui è composta la tessitura. Nelle figure sono presentate rispettivamente l interpretazione delle celle di ogni singolo strato e l aspetto complessivo assunto dalla texture delle masse 13. In figura 4.16 è visualizzata tale struttura. Per quanto riguarda le molle esse perdono, essendo oramai divenuti inu- 13 Usare Eulero come metodo di integrazione e rendere il calcolo dell accelerazione locale al fragment-shader delle masse renderebbe le componenti di accelerazione superflue ma per l espandibilità futura del codice verso altri tipi di integrazione numerica si è scelto di mantenere nella struttura della massa tale attributo.

94 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 91 Figura 4.16: Struttura a strati della texture delle masse. Si noti come nella componente a del livello delle posizioni sia memorizzato l identificativo della massa stessa tile, i riferimenti alle masse; riducendo così la propria struttura a due soli campi: il coefficiente della molla k e l estensione della stessa a riposo. Anche le molle verranno mantenute in una texture il vertex-program Sebbene finora ci si sia concentrati quasi esclusivamente sul fragment-shader non bisogna dimenticare il ruolo del vertex-processor per permettere un corretto svolgimento della computazione. In particolare, per ora, due saranno le operazioni svolte su ogni singolo vertice del quad di attivazione: la trasformazione della sua posizione in coordinate di schermo ed il passaggio delle coordinate di texture al fragment-shader per permetterne l interpolazione. In

95 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 92 struct MassLayer1 { field r: p.x field g: p.y field b: - field a: id } struct MassLayer2 { field r: v.x field g: v.y field b: - field a: - } struct MassLayer3 { field r: a.x field g: a.y field b: - field a: - } struct Spring { field r: k field g: qlength field b: - field r: - } Figura 4.17: texture delle masse Interpretazione per strati dei byte della

96 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 93 figura 4.18 è riportato il codice del vertex-program minimale fin qui definito. varying vec2 texcoordpos; varying vec2texcoordvel; varying vec2texcoordacc; void main(void) { texcoordpos = gl_multitexcoord0.st; texcoordvel = gl_multitexcoord1.st; texcoordacc = gl_multitexcoord2.st; gl_position = ftransform(); } Figura 4.18: Vertex program per il multitexturing necessario per accedere ai vari strati della texture delle masse Il fragment-program Una volta delineata la struttura delle texture sulle quali si andrà ad operare definire un fragment-program che sfrutti il metodo di Eulero per la simulazione di un sistema masse-molle potrebbe apparire una operazione relativamente semplice. In realtà resta ancora un aspetto non secondario da chiarire. Nel passo di integrazione si aggiornano due quantità vettoriali diverse: la posizione e la velocità. Sebbene si sia in un ottica bidimensionale, che permetterebbe quindi di compattare le componenti della posizione e della velocità dentro un unico frammento, si preferisce fin da ora tener separata

97 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 94 la scrittura due attributi fisici. Questo potrebbe far credere che si stia imponendo al fragment-program di dover aggiornare contemporaneamente due quantità vettoriali diverse per singolo frammento; il che come abbiamo visto è impossibile. Per ovviare a tale limite si definiranno due fragment-program distinti operanti in successione: uno per aggiornare la velocità ed uno per la posizione. In realtà non si implementeranno due shader diversi ma semplicemente l applicazione principale si farà carico di passare al fragment-program una variabile booleana che indichi se si stia compiendo l integrazione sulla posizione o sulla velocità. Questo, principalmente, per favorire la performance, in quanto disattivare uno shader e attivarne uno nuovo è un operazione relativamente costosa che costringe la scheda grafica a svuotare la pipeline di rendering prima di passare ad eseguire il nuovo fragment-program. Minimizzare i cambi di contesti tra shader differenti è una delle chiavi per raggiungere più alti gradi di efficienza computazionale. Successivamente sono presentati rispettivamente il codice del fragment program e nella figura 4.20 lo schema generale dell interazione tra applicazione principale e GPU Condizioni al contorno In precedenza si è più volte preannunciato che il calcolo general purpose in GPU possa presentare grandi difficoltà realizzative. È curioso notare come tali problematiche si annidino molto più frequentemente nella scrittura dell applicazione principale che piuttosto nella definizione degli shader veri e propri. È, 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ò rivelarsi esperienza frustrante.

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

99 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à sul frammento la posizione, altrimenti la velocità

100 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 corretta. In questa sezione se ne esporranno le principali, quelle di cui si ha più 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 riguardano 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 epistolare tra l autore e l Nvidia per la segnalazione di bachi nel driver e nel compilatore di OGLSL. A scanso di equivoci si vedrà che L Nvidia stessa riconoscerà 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 sviluppare 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(nextp owoft wo(n), NextP owoft wo(max COLUMN)) (4.2)

101 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 98 row = NextP owoft wo(ceil(n/nextp owoft wo(max COLUMN))) (4.3) Nelle due formule la funzione NextPowOfTwo dato un numero m restituisce il numero successivo ad m che è una potenza di due. La costante MAX COLUMN indica, invece, il numero di colonne massime che una texture secondo le specifiche dell API possa avere. Tipicamente questo valore in OpenGL è di In seguito si evidenzierà che costruire texture lunghe e strette genera un certo beneficio in termini di performance; si sarà, per questo, costretti a considerare più attentamente quale valore porre come limite massimo al numero di colonne per riga. La necessità di costruire texture di dimensioni prefissate costringe ad un piccolo ritocco dell architettura dell applicazione. Precedentemente si è imposto che gli strati della texture abbiano dimensioni uguali tra loro ed ora, per costruzione, si è aggiunto il vincolo che righe e colonne di ogni strato siano in potenza di due. Purtroppo, però, questo non è sufficiente, in quanto anche la texture delle masse nella sua totalità 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ò non è vero, in quanto si hanno tre attributi fisici distinti: posizione, velocità ed accelerazione. Si è già 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à 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à i valori appartenenti a tale insieme sono costruiti su soli 8 bit; assolutamente insufficienti per poter avere una

102 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 è stata introdotta 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, è necessario provvedere al disegno di un rettangolo dalle dimensioni dipendenti dal numero di frammenti su cui si

103 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) permette di determinare una regione rettangolare dello schermo all interno della quale gli elementi tridimensionali da visualizzare verranno disegnati. I parametri x ed y indicano il pixel che diventerà l angolo in basso a sinistra della finestra di renderizzazione; width e height il numero di pixel rispettivamente della larghezza e dell altezza dell area di rendering. Si fa notare che la glviewport non è una funzione che ritaglia il contenuto di una porzione 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 è diverso da width/height allora l immagine finale risulterà distorta (fig). La glviewport fornisce un comodo meccanismo per costruire un quad composto da un numero prefissato di pixel, sarà 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 è 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 è operazione relativamente facile in quanto essendo le coordinate di texture comprese tra zero ed uno ed avendo quattro strati di attributi si ha che

104 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 101 data la cella contenente la posizione di una massa m di coordinate 14 (x,y), la corrispondente velocità avrà coordinate (x,y ), l accelerazione si troverà a (x,y + 0.5) ed, infine, la normale a (x,y ). Per poter rendere agevole il recupero da parte di un frammento delle informazioni sullo stato dei suoi attributi vi è la necessità di avere un rapporto di 1:1 tra pixel del quad ed i texel della texture. Vi deve, cioè, 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 è disposta su n colonne anche il quad dovrà avere le stesse dimensioni. Se, però, 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è 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 vincoli strutturali imposti da OpenGL è quindi necessario costruire un area di viewport, e quindi un quad di attivazione, che sia il più possibile aderente al numero di masse componenti il sistema. In particolare, per ora, si farà in 14 Si ricorda che tipicamente le coordinate delle texture hanno forma (colonna, riga) al contrario di quanto si è soliti fare per l accesso ad una normale matrice.

105 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ù frammenti del necessario

106 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ò sarà sufficiente ridefinire il numero di righe componenti il quad nel seguente modo: not empty row = ceil(n/max COLUMN)) (4.4) In seguito per migliorare ancora l efficienza del calcolo si interverà sul valore MAX COLUMN per minimizzare il numero di fragment che pur ricandendo 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 massa; 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 modifica delle dimensioni del quad imporrà di dover modificare la definizione delle coordinate di texture. In particolare esse diverranno come da figure 4.23 e In figura 4.25 infine è visualizzato il rapporto tra la percentuale di righe di texture not empty e le dimensioni totali della texture stessa Aggiornare la texture delle masse Per questa sezione ci si è accontenterà di scaricare il pixel buffer 15 dalla memoria video verso la memoria principale del calcolatore e di lasciare totalmente alla CPU la fase di dispatching dei valori di posizione, velocità ed accelerazione nelle celle corrispondenti della texture delle masse. Si fa no- 15 Quello che si è fatto finora non era vero e proprio render to texture ma solo una parte di esso. In effetti sarebbe più corretto parlare di render to pixel-buffer. Comunque questo era sufficiente ai fini della trattazione fin qui esposta.

107 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 104 Figura 4.23: Coordinate per il multitexturing del quad di attivazione. Versione efficiente, si attivano solo quei frammenti facenti parte di righe non vuote tare, comunque, che questo dispatching è diverso da quello a cui si sarebbe stati costretti nel caso si fosse perseguita la strategia della CPU. La fase di post-processing di cui si parlava alla sezione prevedeva, infatti, che l unità centrale di elaborazione si facesse carico della somma delle accelerazioni agenti su una massa. Ciò significava lasciare alla CPU una parte della gestione della simulazione fisica; il che cozza con lo spirito di questa trattazione. Stavolta ci si limita solo ad una copia dei valori da una texture o un array temporaneo su cui si sono stati scaricati tramite la funzione di

108 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 105 glviewport(0.0,0.0,col, not_empty_row); glortho(0.0,1.0,0.0,1.0,-1.0,1.0); float not_empty = not_empty_row / row; 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,not_empty); glmultitexcoord2f(1,1.0, not_empty); glmultitexcoord2f(2,1.0, not_empty); glmultitexcoord2f(3,1.0, not_empty); glvertex2f(1.0,1.0); glmultitexcoord2f(0,0.0,not_empty); glmultitexcoord2f(1,0.0, not_empty); glmultitexcoord2f(2,0.0, not_empty); glmultitexcoord2f(3,0.0, not_empty); glvertex2f(0.0,1.0); glend(); Figura 4.24: Definizione a codice delle coordinate di multitexturing

109 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 106 Figura 4.25: Il quad di attivazione classifica i texel in tre sottogruppi: quelli contenenti informazioni utili, quelli contenenti informazioni inutili e non attivati e quelli inutili ma che vengono comunque attivati glpixeltransfer nello strato corrispondente della texture delle masse. Il passaggio dei dati dal pixel-buffer alla memoria principale è, in realtà, costosissima! Si ricordi, infatti, come il bus AGP non fornisca alcuna velocizzazione nello scambio dei dati nella direzione GPU - CPU. Sarebbe assolutamente inutile fare general purpose computation su GPU se la performance guadagnata in termini di operazioni al secondo non si traducesse in una effettiva velocizzazione della computazione, limitata, quest ultima, dal trasferimento dei dati tra le due unità. Per ora tale fase è comunque resa

110 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 107 indispensabile dalla necessità della CPU di leggere le posizioni delle masse per provvedere al disegno delle stesse. Vedremo nelle prossime sezioni come poter ovviare abbastanza facilmente a questo limite. 4.7 Render to texture Per evitare di scaricare le texture dalla memoria video sulla memoria principale OpenGL offre un meccanismo semplice e funzionale alle esigenze del calcolo general purpose su GPU. La procedura glcopytexsubimage2d copia in una texture il contenuto del frame buffer corrente (nel caso in esame il pixel-buffer) senza che sia necessario, per questo, uscire dalla memoria locale alla scheda video. Tra i parametri di input della funzione ce ne sono due che sono particolarmente utili: xoffset e yoffset. Essi sono due interi che indicano il texel della texture da cui cominciare a scrivere il contenuto del pixel-buffer. Si riveleranno fondamentali nel dispatching degli attributi a seconda del valore che debba essere calcolato dallo shader. Ad esempio se si dovesse scrivere nello strato della velocità xoffset dovrebbe essere settato a zero mentre yoffset al valore di row calcolato nell equazione 4.5. Nella figura 4.26 è illustrato un esempio di glcopytexsubimage2d su una texture a due strati. 4.8 Look-up di una texture da vertex program Il render to texture ha senso solo se si ha disposizione un meccanismo che permetta di leggere una texture direttamente dalla memoria locale alla scheda

111 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 108 Figura 4.26: La funzione glcopytexsubimage2d permette di definire un offset all interno di una texture in cui scrivere un array di valori. Ciò è particolarmente utile per la struttura a strati della texture delle masse video 16 e di disegnare le masse tramite le informazioni di posizione conservate nella texture stessa. Come si è visto nel primo capitolo, trasformare la posizione di un vertice nei vari sistemi di coordinate è prerogativa esclusiva del vertex-shader. Ciò implica che il vertex-program abbia accesso agli attributi di un vertice e che possa modificarne i valori definiti dall applicazione principale. Nelle ultimissime generazioni di device grafici il look-up delle texture non è più limitato al solo fragment-program. L Nvidia fx 6800 e la ATI X800 hanno introdotto, infatti, nel vertex-shader una unità di fetching delle texture, permettendo, così, l accesso alle informazioni contenute in esse. Se per attivare il calcolo di un fragment-program è necessario disegnare un quad di dimensioni pari al numero di frammenti che si vogliono processare, permettere ad un vertex-shader di applicare la funzione in esso definita su ogni vertice di uno stream è una operazione molto più naturale. Sarà, infatti, sufficiente caricare il vertex-program nel vertex-processor e disabilitare la 16 Senza, cioè dover scaricare le texture dalla memoria della GPU nella memoria centrale.

112 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 109 pipeline di rendering standard. Da quel momento ogni vertice passato ad OpenGL dall applicazione principale verrà trattato secondo le specifiche dello shader. Resta un unico punto oscuro. La funzione glvertex ha un numero di parametri pari al numero di coordinate dello spazio vettoriale su cui deve operare; tali parametri indicano la posizione del vertice rispetto alle coordinate del mondo. Poichè nel nostro caso la posizione in cui dovremmo disegnare il vertice è contenuta nella texture delle masse, inaccessibile all applicazione principale, è naturale chiedersi con quali parametri si debba invocare la funzione glvertex. In realtà tali parametri non verranno in alcun modo considerati dal vertex-program. Ai fini dell applicazione, è sufficiente che vengano definiti un numero di vertici pari al numero delle masse e che per ogni vertice si passi un attributo contenente l identità del vertice stesso, di modo da permettere allo shader di accedere nella giusta locazione all interno della texture delle masse. Volendo si potrebbe indicare tale indice in uno dei parametri della glvertex, così da risparmiare una ulteriore invocazione di funzione. In figura 4.27 i vertici sono stati prima disegnati tramite glvertex tutti nell origine (uno sopra l altro) poi tramite il vertex-program e la texture da esso letta spostati nella giusta posizione. Il colore diverso di ciascun vertice è la visualizzazione dell identificativo del vertice stesso necessario per il giusto accesso in texture. Il vertex-program di figura 4.28 è un esempio di quanto finora detto. Nel codice mstate rappresenta il riferimento alla texture delle masse e texdim la sua dimensione. In figura, infine, è mostrata la nuova struttura delle interazioni tra applicazione principale e GPU.

113 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 110 Figura 4.27: Per attivare il vertex-shader è sufficiente disegnare tutte le masse nell origine; sarà poi compito del vertex-program leggere la reale posizione degli elementi dalla texture dello stato dell integrazione 4.9 Interazione con il mouse Finora non è stato possibile, vista la struttura del fragment-program, interagire in alcun modo con il sistema masse-molle. Quantomeno per poter testare il programma è necessario aggiungere un minimo di interattività tra il sistema stesso ed il mouse; di modo che sia possibile applicare ad una singola massa una forza parallela alla direzione di spostamento del dispositivo di puntamento. Per determinare, all interno di un insieme di elementi cliccabili, quale primitiva geometrica sia stata effettivamente selezionata dal mouse, OpenGL mette a disposizione una tecnica conosciuta come picking. Ogni qualvolta avvenga un click del mouse, si entra in modalità GL SELECT e si passa ad OpenGL ogni primitiva su cui si voglia effettuare il test di picking, avendo cura, inoltre, di associare alla primitiva stessa un identificativo univoco. In modalità GL SELECT un elemento percorre tutta la pipeline grafica fino alla fase di rastering inclusa. A quel punto l API costruisce un area centrata nella posizione in cui è avvenuto il click del mouse, zoommando su di essa. Se una

114 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 111 uniform vec2 texdim; uniform sampler2d mstate; attribute float index; void main(void) { vec3 point = texture2d(mstate,coordtx(index,texdim)); gl_vertex = vec4(point.xyz,1.0); gl_position = ftransform(); } Figura 4.28: Il codice del vertex-program per il look-up delle texture primitiva ricade all interno di tale area significa che essa è stata cliccata ed OpenGL aggiunge l identificativo precedentemente assegnato alla stessa in un array contenente tutti gli elementi selezionati. Sarà poi compito dell applicazione principale definire il comportamento sugli elementi di tale array. Si fa notare che la modalità GL SELECT non implica alcuna visualizzazione sullo schermo delle primitive su cui si sta effettuando il test di picking. Per poterle disegnare sarà quindi necessario tornare all usuale modalità di GL RENDER e ri-inviare lungo la pipeline tutti vertici che le compongono. Aggiungere il mouse nell applicazione finora delineata, mantendone la filosofia di fondo (tutto il calcolo della simulazione deve essere svolto dalla GPU), non richiede particolari modifiche al codice. In particolare entrando in modalità GL SELECT si attiverà il vertex-shader di look-up sulla texture delle masse e si passeranno alla pipeline grafica tutte quelle su cui si intenda effettuare il test di picking. OpenGL restituirà, quindi, all applicazione principale l identificativo delle masse selezionate. Tra queste si considererà come cliccata solo quella più prossima alla camera virtuale.

115 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 112 A questo punto sarà sufficiente passare al fragment shader, che effettua l integrazione, l identificativo della massa selezionata e la forza che il mouse le impone. Se il frammento che si sta elaborando è quello corrispondente alla massa cliccata, il fragment-shader si farà carico di sommare l accelerazione imposta dal mouse a quella dovuta dalle molle. In figura 4.29 si evidenziano le modifiche che il fragment-program definito in precedenza dovrà subire per tener conto della forza impressa dal mouse. La successiva fase di disegno del sistema, tramite vertex-shader, rimane del tutto invariata Terza dimensione Con la struttura fin qui definita è facile passare da uno spazio a due dimensioni ad uno a tre. Non è necessario alcuna modifica al codice del fragmentprogram diversa dall aggiungere all interno della gl FragColor la componente z di velocità e posizione Complessi simpliciali di dimensione uno Sebbene anche la catena di masse-molle ricada all interno dei complessi simpliciali di dimensione uno, essa ne è solo un caso particolare; in questa sezione ci si prefigge di definire l ossatura di un applicativo operante sul caso generico Texture di connettività Nell implementazione su GPU della catena, si è fatto ampiamente uso dell assunzione che su di una massa incidessero al massimo solo due molle. Nel caso generale di un complesso simpliciale di dimensione uno tale assunzione

116 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 113 uniform bool pos_stage; uniform sampler2d mass; uniform float clicked; uniform vec2 fmouse; varying vec2 texcoordpos; void main(void) { vec4 mspos = texture2d(mass,texcoordpos); float index = mspos.w; //ricavo la velocità della massa vec4 f = vec4(0.0); if (!pos_stage) { if (index > 0.0) { //calcolo la forza imposta dalla molla } } if (index < nmass - 1) { //calcolo la forza imposta dalla molla } if (clicked == index) f = f + fmouse; //integro la velocità tenendo conto della forza del mouse } else { //integro la posizione } Figura 4.29: program Codice integrativo per la gestione del mouse nel fragment

117 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 114 è destinata immediatamente a cadere. Ogni massa, quindi, potrà far capo ad un numero variabile di molle. Data una massa m 1 sarà per questo necessario mantenere informazione esplicita sull identità di una molla s incidente in m 1 e sulla massa m 2 all altro capo di s. Visto che, come al solito, il calcolo della simulazione si dovrà svolgere completamente in GPU, la struttura dati destinata a contenere le informazioni di connettivitvà sarà una texture. Ogni texel 17 di quest ultima manterrà i riferimenti a due coppie di masse-molle incindenti su di una singola massa. Per risparmiare spazio, la texture sarà completamente compattata. Ciò significa che data una massa m con n connessioni, le informazioni di connettività della massa m + 1 inizieranno al texel (fig 4.30) primo texel di(m) + (n/4) + 1. Figura 4.30: Struttura minimale della texture di connetività Tale texture verrà creata nella fase di inizializzazione dell applicazione principale e non verrà più cambiata. 17 Si ricorda che un texel ha al suo interno quattro componenti float; che permetteranno, quindi, di memorizzare informazioni su due connessioni distinte. Una connessione è una coppia molla-massa opposta.

118 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Accedere alla texture Per accedere correttamente alla texture della connettività sarà necessario che ogni massa m mantenga informazione su quante siano le molle che in essa incidono e l indice del texel contenente la prima di queste molle Massa Per contenere le due informazioni appena introdotte è necessario rivisitare la struttura di MassLayer2 e MassLayer3. In particolare nella componente a dello strato delle velocità si manterrà il numero di molle connesse alla massa e nello strato delle accelerazioni l indice del texel contenente la prima connessione. struct MassLayer2 { field r: v.x field g: v.y field b: v.z field a: nconn } struct MassLayer3 { field r: a.x field g: a.y field b: a.z field a: offset }

119 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU Fragment-program Di qualche modifica in più necessita il fragment-program. In particolare, per ottenere la forza totale agente su di una massa m, si dovrà ciclare su tutte le molle che incidono sulla massa stessa. Per recuperare le informazioni di tali molle e delle massa h opposta ad m si accederà alla texture di connettività definita in precedenza. Nel fragment-program si evidenzia un forte limite che OGLSL impone alla leggibilità del sorgente e all efficienza dell applicazione: l impossibilità di indirizzare array e vettori con un indice di displacement il cui valore non sia noto a compile-time. Ad esempio nel codice di figura 4.32 non si potrà accedere direttamente alla porzione di texel 18 contenente la coppia massamolla d interesse ma sarà necessario fare un if per determinare se si debba effettuare il calcolo sulla prima o sulla seconda coppia contenuta nella cella. Nelle figure 4.31 e 4.32 sono presentati rispettivamente un esempio di come il frammento relativo alla massa 21 ottenga le informazioni necessarie al fragment-shader di integrazione e il codice del fragment-shader stesso Complessi simpliciali di dimensione due Per implementare i complessi simpliciali di dimensione due e tre si seguirà una nuova linea architettonica che permetterà di eliminare dinamicamente i simplessi che li compongono. Cancellare una molla dal complesso simpliciale di dimensione uno definito finora non è operazione agevole, in quanto non esiste una texture che mantenga, centralizzate, le istanza dei segmenti. Esse, invece, sono presenti in 18 Si ricorda che un ogni texel contiene due coppie molla-massa opposta. La coppia pari si troverà nei membri r e g; quella dispari in b ed a.

120 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 117 Figura 4.31: Il frammento connesso con la massa 21 compie i look-up sulle varie texture per permettere la collezione delle informazioni per l integrazione. I colori nella massa di connettività indicano le molle incidenti sulla massa doppia copia (una per ogni massa attaccata agli estremi di una molla) all interno della texture di connettività. Con l architettura attuale, per eliminare una molla s, sarebbe necessario risalire alle masse che condividono tale molla e cancellare per ognuna di queste i riferimenti ad s contenuti nella texture di connessione. Il costo dell operazione sarebbe lineare sulle dimensioni di tale texture. Se invece si avesse una texture centralizzata cancellare la molla s avrebbe un costo costante.

121 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 118 uniform bool pos_stage; uniform sampler2d mass,spring, con; uniform float dt, damp, nmass; uniform vec2 dimmass, dimspring, dimconn; varying vec2 texcoordpos,texcoordvel, texcoordacc; void main(void) { vec4 mspos = texture2d(mass,texcoordpos); float index = mspos.w; mspos.w = 0.0; vec4 msvel = texture2d(mass,texcoordvel); float nconn = msvel.w; msvel.w = 0.0; vec4 msacc = texture2d(mass,texcoordacc); float offset = msacc.w; msacc.w = 0.0; vec4 f= vec4(0.0); if (!pos_stage) { int i = 0; float h = 0.0; while(i < nconn) { vec4 tot_ind = texture2d(con,coordtx(offset + h,dimconn)); vec2 ind; if (mod(h,2.0) == 0.0){ ind.x = tot_ind.x; ind.y = tot_ind.y; } else{ ind.x = tot_ind.z; ind.y = tot_ind.w; } if (ind.x!= -1.0){ vec4 s1 = texture2d(spring,coordtx(ind.x,dimspring)); vec4 m1 = texture2d(mass,coordtx(ind.y,dimmass)); vec4 len1 = m1 - mspos; vec4 fs = s1.r * (length(len1) - s1.g) * normalize(len1) - damp * msvel; f = f + fs; } ++i; h = h + 1.0; } vec4 vel = msvel + fs * dt; gl_fragcolor = vec4(vel.x,vel.y,vel.z,nconn); } else { vec4 pos = mspos + msvel * dt; gl_fragcolor = vec4(pos.x,pos.y,pos.z,index); } } Figura 4.32: Il fragment program che implementa la gestione del sistema masse molle per complessi simpliciali di dimensione uno

122 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 119 Si definisce, quindi, per il simplesso di grado due una nuova texture triang contenente tutti i triangoli componenti il complesso simpliciale. Inoltre si modifica leggermente il significato della texture di connessione. In essa non ci saranno più riferimenti a masse e molle ma solo gli indici dei triangoli che incidono in una massa m. Sarà solo tramite questi che m potrà risalire alla massa h attaccata dall altra parte della molla. Infine si rende necessaria una ridefinizione della texture delle molle. Per comodità si supporrà il coefficiente k come una costante condivisa da ogni molla componente il sistema e si lasceranno all interno della texture le sole lunghezze a riposo di ognuno dei tre lati del triangolo. Tali lunghezze saranno memorizzate all interno dei campi di un texel seguendo un ordine stretto di precedenza sui lati del triangolo (fig 4.34). Nel seguito ci si riferirà a questa texture come qlength. Le dimensioni di qlength saranno pari a quelle di triang generando, cosi, una corrispondenza immediata tra il texel di un triangolo e il corrispettivo texel di qlength. In figura sono raffigurate le caratteristiche appena elencate delle varie texture. Non viene invece modificata in alcun modo l interpretazione fornita precedentemente sugli strati della texture delle masse Fragment-program Dato un frammento relativo ad una massa m si accede tramite l offset ed il numero di triangoli connessi ad m alla texture della connettività. Con gli indici dei triangoli incidenti nella massa, si arriva facilmente ai simplessi contenuti nella texture triang. Tramite triang si ottengono le tre masse che sono ai vertici del triangolo. Tra queste, però, c è sicuramente la massa m che sta effettuando il calcolo; anche se è ignoto ad m stessa in quale componente

123 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 120 è stata memorizzata. Ovviamente non essendoci alcuna molla tra una massa e se stessa si evita di occupare la GPU con calcolo inutile, e ci si concentra invece sulle altre due masse. Con gli indici trovati in triang si accede alla texture delle masse dove si ottengono le informazioni riguardanti la posizione degli elementi che partecipano con m alla definizione del triangolo (fig 4.33). Figura 4.33: Schema di computazione per i complessi simpliciali di dimensione due. Si noti la presenza della texture centralizzati dei triangoli A questo punto per definire la forza generata su m dalla molla appesa su uno dei lati del triangolo sarebbe sufficiente recuperare l informazione sulla lunghezza a riposo della molla stessa. Trovare il texel corrispondente all interno della texture di qlength è operazione facile. Esso avrà le stesse

124 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 121 coordinate del triangolo all interno della texture di triang. Purtroppo però non esiste un meccanismo semplice ed elegante per determinare facilmente quale tra le tre lunghezze di riposo contenute nel texel tx di qlength sia quella che si riferisca alla molla su cui si sta operando. Si consideri la figura Il numero in nero sulle masse rappresenta il loro identificativo, il numero in rosso il loro indice relativo al triangolo. All interno della texture triang, nel texel riguardante il triangolo in grigio, le masse sono state memorizzate seguendo l ordine imposto da tale indice. In qlength, nella fase dell inizializzazione dell applicazione principale, le estensioni a riposo delle molle costruite sui lati del triangolo, sono state a loro volta salvate seguendo un rigido ordinamento; nella componente r del texel t la lunghezza riguardante il lato 0-1 (quello che in figura unisce le masse 42 e 40), in g 0-2 e in b Si considerino le masse 40 e 38, che formano il segmento 1-2. Per poter accedere alla loro lunghezza a riposo, che, come abbiamo appena visto, per costruzione è contenuta nella terza componente di t, si deve sapere quale sia il loro indice relativo al triangolo in questione. Per far questo, purtroppo, non vi è altro modo che fare un ricerca all interno delle quattro componenti del texel dell identificativo della massa. Una volta ottenuta tale informazione, non vi è alcuna difficoltà nel completare il processo di integrazione numerica e far avanzare la simulazione del sistema masse-molle su qualsiasi complesso simpliciale di dimensione due. 19 Si specifica che tale ordine non è relativo solo al triangolo in figura ma va esteso ad ogni triangolo componente la mesh.

125 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 122 Figura 4.34: Ordine della texture delle lunghezze a riposo delle molle 4.13 Complessi simpliciali di dimensione tre La struttura dei complessi simpliciali di tetraedri è speculare a quanto appena visto per i triangoli. Invece della texture dei triangoli si avrà quella contenente tetraedri (tetra) e la tessitura qlength, avendo un tetraedro sei lati 20, avrà uno dimensione doppia rispetto a quella di tetra. Nelle figure 4.35 e 4.36 sono rappresentati le versioni delle immagini 4.33 e 4.34 relative ad una mesh di tetraedri. 20 Si ricorda che ogni texel contiene quattro float; saranno quindi necessari due texel per memorizzare le lunghezze a riposo delle molle componenti il tetraedro.

126 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 123 Figura 4.35: Ciclo computazionale per i tetraedri. I colori nella texture di connettività indicano il tetraedro incidente in quella massa 4.14 Eliminare simplessi Nel codice del fragment program il comando if (any(greaterthanequal(tetra,vec4(0.0)))) serve a verificare se il tetraedro su cui si sta operando sia ancora valido o se invece sia stato precedentemente rimosso. Per segnare che un tetraedro (triangolo) non debba più essere preso in considerazione nel calcolo delle forze agenti su di una massa si settano gli indici di riferimento alle masse contenuti nella texture tetra (triang) con valori negativi. Quando il fragment-shader accede ad un tetraedro (triangolo) che è stato segnato come eliminato lo ignora completamente.

127 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 124 Figura 4.36: Ordine delle lunghezze a riposo nella texture Qlength Per cancellare i tetraedri si definisce un fragment-program operante sul singolo tetraedro che, in un dato istante, si deve eliminare. Si costruisce, cioè, un quad grande quanto un solo pixel e si assegna ai suoi vertici le coordinate della texture tetra relative al simplesso da segnare come cancellato. Tale shader è riportato in figura Si fa notare come usando tale approccio i tetraedri pur non venendo più considerati nel calcolo sulla simulazione totale del sistema in realtà sono ancora presenti all interno della texture e, se si volesse, sarebbe sufficiente riapplicare lo shader per recuperarli. Inoltre si sottolinea come tale shader vale tanto per i tetraedri quanto per i triangoli.

128 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 125 varying vec2 texcoorddel; uniform sampler2d tetra; void main(void) { vec4 tt = texture2d(tetra,texcoorddel); gl_fragcolor = -tt; } Figura 4.37: Fragment shader per l eliminazione dei simplessi In figura 4.38 è mostrata la cancellazione di un tetraedro dal punto di vista della texture dei simplessi.

129 CAPITOLO 4. COMPLESSI SIMPLICIALI IN GPU 126 Figura 4.38: Rimozione di un tetraedro dalla simulazione

130 Capitolo 5 Risultati sperimentali e sviluppi futuri In quest ultimo capitolo verranno presentate alcune tabelle di confronto sui risultati raggiunti dall applicativo operante in GPU sviluppato parallelamente alla suddetta trattazione. Come controparte CPU al simulatore introdotto nei capitoli precedenti è stato scelto il motore di integrazione contenuto all interno della libreria del Visual Computing Group del CNR di Pisa. Tutti i test successivi avranno come indice della performance il numero di frame per second raggiunto dalle due applicazioni su complessi simpliciali di varie dimensioni. Purtroppo, a causa di un problema implementativo nel compilatore di OGLSL, ci si è dovuti limitare all analisi sperimentale dei risultati forniti dal solo metodo di integrazione di Eulero. Nell appendice A è riportata la mail in cui i programmatori della Nvidia riconoscono la responsabilità. Ad oggi non è stata ancora rilasciata una versione dei driver che risolva il suddetto problema. I test sono stati tutti condotti su un Athlon a 64 bit con un giga di RAM, 127

131 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI 128 con bus AGP 8x ed una scheda GeForce FX 6800 con 256 Mbyte di memoria dedicata. 5.1 Tabelle dei risultati I risultati presentati sul complesso simpliciale di dimensione uno sono derivati da una rete bidimensionale di masse (figura A.3). In ciascuna massa tipicamente incidono dodici molle la cui disposizione rispetto la masse stessa è illustrata nella figura. Figura 5.1: Connessione di un singolo elemento della rete Sebbene una rete come quella appena presentata abbia una connettività regolare, tanto da poter essere supposta implicita, per rendere il test

132 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI complesso simpliciale 2D masse segmenti GPU (fps) CPU (fps) Figura 5.2: Risultati per il complesso simpliciale di dimensione uno in 2D significativo si è comunque considerata la versione facente uso della texture che mantenga informazione esplicita sulle molle incidenti in una massa. La tabella di figura 5.2 riassume i risultati ottenuti in termini di frame per second. Le mesh campione usate per i test sui complessi simpliciali di dimensione due sono solidi tridimensionali di varia natura: tetraedri, icosaedri, coni e cilindri. Nella sezione in cui si mostrano alcuni screenshot dell applicazione sviluppata si possono vedere alcune loro immagini mentre vengono deformati dal simulatore. I risultati ottenuti su tali solidi sono presentati nella tabella 5.3. Per i complessi simpliciali su tetraedri a grado di connettività omogeneo (mesh in cui non esistono masse che hanno un numero di tetraedri incidenti in esse molto superiori alle altre) è stato scelto un cubo a vari gradi di risoluzione. Nella tabella 5.4 sono illustrati i risultati sui test ottenuti. Infine per le mesh tetraedrali star-shaped (mesh in cui esiste un punto centrale su cui incidono tutti i simplessi componenti il complesso simpliciale) si è scelta come oggetto di riferimento per i test, i cui risultati sono riportati in tabella??, una sfera a diversi gradi di risoluzione. Nella sezione successiva

133 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI complesso simpliciale masse triangoli GPU (fps) CPU (fps) Figura 5.3: Risultati per il complesso simpliciale di dimensione due in 3D sarà chiarito perchè si e preferito tenere divise le due ultime tabelle. 5.2 Commenti sui risultati ottenuti Tutti gli esperimenti condotti su complessi simpliciali a dimensione diversa e a diverso grado di connettività hanno dato esito positivo con un buon grado di incremento della performance totale. Come si evince in alcuni test si è spesso avuto un aumento delle prestazioni del 100%. Un solo test ha ottenuto risultati in controtendenza: quello condotto su una mesh tetraedrale con una connotazione topologica a stella; cioè una mesh in cui esiste un punto centrale che è condiviso da tutti i simplessi della mesh stessa. Sebbene come si è detto tale risultato non segua la tendenza imposta dagli altri test la cosa non è risultata in alcun modo sorprendente. Nel secondo capitolo si sottolineato come il fragment-processor abbia una architettura di tipo SIMD, in cui come è stato specificato, tutti i fragment-processor operanti in parallelo debbano svolgere in un dato istante la stessa istruzione. Nel caso di una mesh tetraedrale star-shaped vi è un frammento, quello che si riferisce alla massa in cui incidono tutti i tetraedri del complesso simpliciale, che agisce

134 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI complesso simpliciale masse triangoli GPU (fps) CPU (fps) Figura 5.4: Risultati per il complesso simpliciale su tetraedri. Tali risultati si riferiscono a mesh a connettività omogenea da freno sull avanzamento complessivo del calcolo; costringendo la GPU ad interrompere la computazione sugli altri frammenti che operano in parallelo a quello. Le mesh star-shaped, data l architettura della GPU e il meccanismo degli shader, rappresentano casi patologici difficili da superare. Si fa presente come invece l implementazione su CPU non sia soggetta a tale problema, poichè il suo ciclo di aggiornamento delle forze non avviene per masse ma per molle. Dal punto di vista del processore centrale c è solo un aumento 3-complesso simpliciale star-shaped masse triangoli GPU (fps) CPU (fps) Figura 5.5: Risultati per il complesso simpliciali tetraedrali star-shaped, ovvero con una massa centrale su cui incidono tutti i tetraedri

135 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI 132 del numero delle molle totali che compongono il complesso simpliciale. La CPU, come è stato sottolineato, presenta una quantità molto maggiore di transistor dedicati al controllo sul flusso dei dati e strategie più raffinate di risoluzione e predizione sul codice condizionale di quante ne esponga la GPU. Tale caso rientra quindi nei limiti patologici sofferti dall architettura delle schede grafiche, che è bene ricordarlo sono state progettate con ottiche estranee al calcolo general purpose. Si fa comunque notare che con un forte l aumento del numero dei tetraedri e il conseguente incremento della quantità di calcolo, come risulta dall ultima linea della tabella il grado di performance della GPU torni a sovrastare la CPU, riesca ad ammortizzare la latenza dovuto al frammento centrale. Si nota inoltre dalle tabelle come il test condotto sul complesso simpliciale di dimensione uno abbia ottenuto i risultati più omogenei rispetto al numero delle masse e dei segmenti/molle, cosa che non si riscontra negli altri due casi. Seppur questo possa apparire sorprendente si ricorda che l implementazione del complesso simpliciale di dimensione uno presenta caratteristiche differenti rispetto a a quelli di dimensione due e tre. In particolare in questi ultimi vi è un numero molto maggiore di accessi in memoria. Rimane, comunque, una certa tendenza asintotica comune, che fa in modo che all aumentare dei simplessi la simulazione su GPU tenda a raddoppiare le prestazioni offerte dalla CPU. I risultati raccolti danno buone ragioni per credere che, l implementazione di un metodo di integrazione numerica più raffinato e quindi più costoso rispetto al semplice metodo di Eulero, acuirà ulteriormente il divario in termini di performance che esiste tra implementazione in CPU e in GPU. Tale convinzione nasce dal fatto che molto del beneficio computazionale dovuto all architettura dei device grafici viene perso nella fase di start-up di uno

136 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI 133 shader. Talvolta fragment-program molto complessi possono presentare prestazioni prossime a quelle di uno shader minimale. A titolo di esempio, allorquando si risolveva il caso dei complessi simpliciali di dimensione uno, e stato paragonato il costo di tale shader con quello di uno che semplicemente accedeva in memoria texture e riscriveva il valore letto dalla stessa. Si è notato uno svantaggio (costante rispetto al numero delle masse del sistema) da parte del fragment-program che operava l integrazione di pochissime unità di fps. 5.3 Sviluppi futuri Gli sviluppi futuri non possono prescindere dall implementazione, appena possibile, di metodi di integrazione numerica più sofisticati e più accurati rispetto ad Eulero. Runge-Kutta del quarto ordine e metodi impliciti sono i primi candidati. Senza contare che il sistema masse e molle come quello che è stato presentato in questo lavoro fornisce solo un primo approccio nella gestione degli oggetti deformabili. Modelli di calcolo come il Finite element method che forniscono approssimazioni di oggetti fisici ad un più elevato grado di realismo al costo di una complessità computazionale molto maggiore; il che li rende, per questo, adatti ad essere implementati su un architettura, come quella della GPU, che sostiene un elevato numero di frame al secondo. Una delle sfide che si ha sicuramente intenzione di raccogliere è la ricerca di metodologie ed algoritmi per la rappresentazione implicita di mesh simpliciali non banali, di modo da poter abbandonare la texture della connettività. Questo da solo dovrebbe garantire un più elevato grado di performance, in quanto diminuirebbero notevolmente gli accessi in memoria texture.

137 CAPITOLO 5. RISULTATI SPERIMENTALI E SVILUPPI FUTURI Conclusioni Questa tesi ha avuto come principale scopo progettare un framework per l implementazione di algoritmi di simulazione dinamica su complessi simpliciali su Graphical Processing Unit (GPU). La peculiaritá dell architettura delle GPU, pensata per velocizzare il processo di visualizzazione, pone molti ostacoli alla realizzazione di algoritmi e strutture dati General Purpose, sia per l impiego di memoria specializzata, nei formati, nei metodi di allocazione e lettura e nelle dimensioni, per contenere immagini, sia per il paradigma di programmazione offerto. Questi tesi ha dimostrato come, utilizzando i complessi simpliciali come struttura di base per la rappresentazione geometrica sia possibile implementare algoritmi computazionalmente pesanti, quali quelli usati per la modellazione real-time di oggetti deformabili, su GPU. I test sperimentali effettuati hanno dimostrato che l utilizzo della GPU in questo contesto porta un effettivo guadagno in termini di tempo di calcolo rispetto ad una tradizionale implementazione su CPU.

138 Appendice A Screenshot del simulatore Figura A.1: Deformazione di un cilindro tramite l applicativo sviluppato parallelamente alla trattazione in esame 135

139 APPENDICE A. SCREENSHOT DEL SIMULATORE 136 Figura A.2: Icosaedri e coni

140 APPENDICE A. SCREENSHOT DEL SIMULATORE 137 Figura A.3: dimensioni La rete usata per i test sui 1-complessi simpliciali in due

Fondamenti di Grafica Tridimensionale

Fondamenti di Grafica Tridimensionale Fondamenti di Grafica Tridimensionale La Pipeline Grafica Marco Di Benedetto marco.dibenedetto@isti.cnr.it Visualizzazione dell Informazione noi siamo qui Informazione mondo reale (es: scans) creazione

Dettagli

Introduzione al Many/Multi-core Computing

Introduzione al Many/Multi-core Computing Introduzione al Many/Multi-core Computing Sistemi Operativi e reti 6 giugno 2011 Outline del corso Introduzione Definizioni Motivazioni Storia Architettura Framework Algoritmica Parte I Introduzione Definizioni

Dettagli

Fondamenti di Grafica Tridimensionale

Fondamenti di Grafica Tridimensionale Fondamenti di Grafica Tridimensionale La Pipeline Grafica Marco Di Benedetto marco.dibenedetto@isti.cnr.it Visualizzazione dell Informazione noi siamo qui Informazione mondo reale (es: 3D scans) creazione

Dettagli

Linguaggi, Traduttori e le Basi della Programmazione

Linguaggi, Traduttori e le Basi della Programmazione Corso di Laurea in Ingegneria Civile Politecnico di Bari Sede di Foggia Fondamenti di Informatica Anno Accademico 2011/2012 docente: Prof. Ing. Michele Salvemini Sommario Il Linguaggio I Linguaggi di Linguaggi

Dettagli

Università degli studi di Roma Tor Vergata Ingegneria Medica Informatica I Programma del Corso

Università degli studi di Roma Tor Vergata Ingegneria Medica Informatica I Programma del Corso Obiettivi Di seguito vengono riportate una serie di domande che possono essere poste durante la prova formale del corso. Le seguenti domande non sono da ritenersi esaustive ma esemplificative. 1. Architettura

Dettagli

Caveat. Sintesi di Immagini. Sintesi di Immagini. Rendering: Approccio Fisico

Caveat. Sintesi di Immagini. Sintesi di Immagini. Rendering: Approccio Fisico Sintesi di Immagini Metafora fondamentale Object vs viewer Object (scene): rappresentazione digitale (forma e caratteristiche) di un oggetto reale tridimensionale Viewer: strumento che permette di otternere

Dettagli

Caveat. Object e viewer, come tutte le metafore, sono entità non definite rigidamente. La luce fa parte del viewer? Il viewer è anch esso un object?

Caveat. Object e viewer, come tutte le metafore, sono entità non definite rigidamente. La luce fa parte del viewer? Il viewer è anch esso un object? Sintesi di Immagini Metafora fondamentale Object vs viewer Object (scene): rappresentazione digitale (forma e caratteristiche) di un oggetto reale tridimensionale Viewer: strumento che permette di otternere

Dettagli

HIGH PERFORMANCE COMPUTING SU UNITA' GRAFICHE PROGRAMMABILI

HIGH PERFORMANCE COMPUTING SU UNITA' GRAFICHE PROGRAMMABILI ALMA MATER STUDIORUM UNIVERSITA' DI BOLOGNA SEDE DI CESENA FACOLTA' DI SCIENZE MATEMATICHE, FISICHE E NATURALI CORSO DI LAUREA IN SCIENZE DELL'INFORMAZIONE HIGH PERFORMANCE COMPUTING SU UNITA' GRAFICHE

Dettagli

Lezione 1. Sistemi operativi. Marco Cesati System Programming Research Group Università degli Studi di Roma Tor Vergata.

Lezione 1. Sistemi operativi. Marco Cesati System Programming Research Group Università degli Studi di Roma Tor Vergata. Lezione 1 Sistemi operativi 4 marzo 2014 System Programming Research Group Università degli Studi di Roma Tor Vergata SO 14 1.1 Di cosa parliamo in questa lezione? È una introduzione generale ai sistemi

Dettagli

UML Introduzione a UML Linguaggio di Modellazione Unificato. Corso di Ingegneria del Software Anno Accademico 2012/13

UML Introduzione a UML Linguaggio di Modellazione Unificato. Corso di Ingegneria del Software Anno Accademico 2012/13 UML Introduzione a UML Linguaggio di Modellazione Unificato Corso di Ingegneria del Software Anno Accademico 2012/13 1 Che cosa è UML? UML (Unified Modeling Language) è un linguaggio grafico per: specificare

Dettagli

Obiettivi. Costruzione di interfacce. Esame. Programma. Strumenti. Concetti di base

Obiettivi. Costruzione di interfacce. Esame. Programma. Strumenti. Concetti di base Obiettivi Costruzione di interfacce Paolo Cignoni p.cignoni@isti.cnr.it http://vcg.isti.cnr.it/~cignoni Progettazione e realizzazione di applicazioni interattive, con un interfaccia utente non banale che

Dettagli

Università degli Studi di Ferrara

Università degli Studi di Ferrara Università degli Studi di Ferrara Corso di Laurea in Matematica - A.A. 2018 2019 Programmazione Lezione 19 Controllo di Flusso in MATLAB Docente: Michele Ferrari - michele.ferrari@unife.it Nelle lezioni

Dettagli

Architetture Applicative Altri Esempi

Architetture Applicative Altri Esempi Architetture Applicative Altri Esempi Alessandro Martinelli alessandro.martinelli@unipv.it 15 Aprile 2014 Architetture Applicative Altri Esempi di Architetture Applicative Architetture con più Applicazioni

Dettagli

Corso integrato di Sistemi di Elaborazione. Modulo I. Prof. Crescenzio Gallo.

Corso integrato di Sistemi di Elaborazione. Modulo I. Prof. Crescenzio Gallo. Corso integrato di Sistemi di Elaborazione Modulo I Prof. Crescenzio Gallo crescenzio.gallo@unifg.it Architettura dei calcolatori 2 Architettura di un calcolatore Che cos è un calcolatore? Come funziona

Dettagli

MATRICE TUNING competenze versus unità didattiche, Corso di Laurea in Informatica (classe L-31), Università degli Studi di Cagliari

MATRICE TUNING competenze versus unità didattiche, Corso di Laurea in Informatica (classe L-31), Università degli Studi di Cagliari A: CONOSCENZA E CAPACITA DI COMPRENSIONE Conoscere e saper comprendere i fondamenti della matematica discreta (insiemi, interi, relazioni e funzioni, calcolo combinatorio) Conoscere e saper comprendere

Dettagli

Corso di Grafica Computazionale

Corso di Grafica Computazionale Corso di Grafica Computazionale Real-Time Rendering Introduzione all hardware grafico Docente: Massimiliano Corsini Laurea Specialistica in Ing. Informatica Università degli Studi di Siena Hardware Grafico

Dettagli

Strutture dati e loro organizzazione. Gabriella Trucco

Strutture dati e loro organizzazione. Gabriella Trucco Strutture dati e loro organizzazione Gabriella Trucco Introduzione I linguaggi di programmazione di alto livello consentono di far riferimento a posizioni nella memoria principale tramite nomi descrittivi

Dettagli

Programmi e Oggetti Software

Programmi e Oggetti Software Corso di Laurea Ingegneria Civile Fondamenti di Informatica Dispensa 06 Programmi e Oggetti Software Marzo 2010 Programmi e Oggetti Software 1 Contenuti Cosa è un programma Cosa significa programmare Il

Dettagli

Rendering 13/01/2014. Marco Tarini - Video Game Dev - Univ Insubria (recall?) Game Engine

Rendering 13/01/2014. Marco Tarini - Video Game Dev - Univ Insubria (recall?) Game Engine Rendering (recall?) Game Engine Parte del game che si occupa di alcuni dei task comuni Scena / livello Renderer Real time transofrm + lighting Models, materials Phsics engine (soft real-time) newtonian

Dettagli

Indice. Prefazione. 3 Oggetti e Java 53

Indice. Prefazione. 3 Oggetti e Java 53 Prefazione xv 1 Architettura dei calcolatori 1 1.1 Calcolatori e applicazioni 1 1.1.1 Alcuni esempi di applicazioni 3 1.1.2 Applicazioni e interfacce 4 1.2 Architettura dei calcolatori 7 1.2.1 Hardware

Dettagli

Il computer. Il case e l'unità di elaborazione. Il computer, una macchina aggiornabile.

Il computer. Il case e l'unità di elaborazione. Il computer, una macchina aggiornabile. Materia: INFORMATICA Classe 1Q Prof.ssa Mazzi Cinzia IL COMPUTER Hardware e software. Il computer. Il case e l'unità di elaborazione. Il computer, una macchina aggiornabile. Le parti che formano un computer.

Dettagli

Linguaggi di Programmazione

Linguaggi di Programmazione Linguaggi di Programmazione!paradigmi linguistici, costrutti!semantica!implementazione, strutture a tempo di esecuzione 1 Linguaggi di programmazione e astrazione! i linguaggi di programmazione ad alto

Dettagli

Informatica 3. Informatica 3. Lezione 1- Modulo 1. LEZIONE 1: Introduzione. Concetti di linguaggi di programmazione. Introduzione

Informatica 3. Informatica 3. Lezione 1- Modulo 1. LEZIONE 1: Introduzione. Concetti di linguaggi di programmazione. Introduzione Informatica 3 Informatica 3 LEZIONE 1: Introduzione Lezione 1- Modulo 1 Modulo 1: Introduzione al corso Modulo 2: Introduzione ai linguaggi di Introduzione al corso Politecnico di Milano - Prof. Sara Comai

Dettagli

Corso di Tecniche Avanzate per la Grafica Texturing

Corso di Tecniche Avanzate per la Grafica Texturing Corso di Tecniche Avanzate per la Grafica Texturing Docente: Massimiliano Corsini Laurea Specialistica in Informatica Università di Ferrara Texturing Il concetto di texturing è importante Si tratta di

Dettagli

Problemi, algoritmi, calcolatore

Problemi, algoritmi, calcolatore Problemi, algoritmi, calcolatore Informatica e Programmazione Ingegneria Meccanica e dei Materiali Università degli Studi di Brescia Prof. Massimiliano Giacomin Problemi, algoritmi, calcolatori Introduzione

Dettagli

Computer Graphics. Digital representation of 3D objects. 3D Rendering. Scena 3D rendering image. Marco Tarini - univ insubria AA 2016/2017

Computer Graphics. Digital representation of 3D objects. 3D Rendering. Scena 3D rendering image. Marco Tarini - univ insubria AA 2016/2017 Computer Graphics Digital representation of 3D objects Università dell Insubria Facoltà di Scienze MFN di Varese Corso di Laurea in Informatica Anno Accademico 2016/17 Marco Tarini 3D Rendering Scena 3D

Dettagli

Il primo programma C++

Il primo programma C++ Il primo programma C++ Un programma in qualsiasi linguaggio evoluto è una sequenza di istruzioni che la CPU dopo opportune conversioni esegue. La sintassi dei linguaggi di programmazione è molto piu rigida

Dettagli

Le classi in java. Un semplice programma java, formato da una sola classe, assume la seguente struttura:

Le classi in java. Un semplice programma java, formato da una sola classe, assume la seguente struttura: Le classi in java Un semplice programma java, formato da una sola classe, assume la seguente struttura: class Domanda static void main(string args[]) System.out.println( Quanti anni hai? ); La classe dichiarata

Dettagli

Programmi e Oggetti Software

Programmi e Oggetti Software Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1 Dispensa 2 Programmi e Oggetti Software Alfonso Miola Settembre 2007 http://www.dia.uniroma3.it/~java/fondinf1/ Programmi e Oggetti Software

Dettagli

Informatica 3. LEZIONE 1: Introduzione. Modulo 1: Introduzione al corso Modulo 2: Introduzione ai linguaggi di programmazione

Informatica 3. LEZIONE 1: Introduzione. Modulo 1: Introduzione al corso Modulo 2: Introduzione ai linguaggi di programmazione Informatica 3 LEZIONE 1: Introduzione Modulo 1: Introduzione al corso Modulo 2: Introduzione ai linguaggi di Informatica 3 Lezione 1- Modulo 1 Introduzione al corso Introduzione Corso di Informatica 3

Dettagli

Il linguaggio di programmazione Python

Il linguaggio di programmazione Python Università Roma Tre Dipartimento di Matematica e Fisica Percorso Abilitante Speciale Classe A048 Matematica Applicata Corso di Informatica Il linguaggio di programmazione Python Marco Liverani (liverani@mat.uniroma3.it)

Dettagli

Programma del corso. Elementi di Programmazione. Introduzione agli algoritmi. Rappresentazione delle Informazioni. Architettura del calcolatore

Programma del corso. Elementi di Programmazione. Introduzione agli algoritmi. Rappresentazione delle Informazioni. Architettura del calcolatore Programma del corso Introduzione agli algoritmi Rappresentazione delle Informazioni Architettura del calcolatore Reti di Calcolatori Elementi di Programmazione Algoritmi e programmi Algoritmo Sequenza

Dettagli

Algoritmo. La programmazione. Algoritmo. Programmare. Procedimento di risoluzione di un problema

Algoritmo. La programmazione. Algoritmo. Programmare. Procedimento di risoluzione di un problema Algoritmo 2 Procedimento di risoluzione di un problema La programmazione Ver. 2.4 Permette di ottenere un risultato eseguendo una sequenza finita di operazioni elementari Esempi: Una ricetta di cucina

Dettagli

Davide Cervi Classe : 3E I.T.E Agostino Bassi - Lodi

Davide Cervi Classe : 3E I.T.E Agostino Bassi - Lodi INTRODUZIONE AI LINGUAGGI DI PROGRAMMAZIONE Davide Cervi Classe : 3E I.T.E Agostino Bassi - Lodi I LINGUAGGI DI PRIMA GENERAZIONE rientrano in questa categoria i vari linguaggi macchina proprietari, decisamente

Dettagli

La trasformazione di camera

La trasformazione di camera La trasformazione di camera 1 Introduzione Per rappresentare un oggetto tridimensionale nello spazio (scena) in un piano bidimensionale (spazio delle immagini, quale il monitor o un foglio) è necessario

Dettagli

In passato, occuparsi di informatica era sinonimo di programmare computer

In passato, occuparsi di informatica era sinonimo di programmare computer Programmare =? In passato, occuparsi di informatica era sinonimo di programmare computer attività poco stimolante, atto finale di un processo dove le fasi creative - analisi e progetto - sono già avvenute

Dettagli

o Introduzione agli algoritmi o Rappresentazione delle Informazioni o Architettura del calcolatore o Reti di Calcolatori

o Introduzione agli algoritmi o Rappresentazione delle Informazioni o Architettura del calcolatore o Reti di Calcolatori Programma del corso o Introduzione agli algoritmi o Rappresentazione delle Informazioni o Architettura del calcolatore o Reti di Calcolatori o Elementi di Programmazione Algoritmi e programmi o Algoritmo

Dettagli

Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE. Francesco Tura. F. Tura

Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE. Francesco Tura. F. Tura Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE Francesco Tura francesco.tura@unibo.it 1 Lo strumento dell informatico: ELABORATORE ELETTRONICO [= calcolatore = computer] Macchina multifunzionale Macchina

Dettagli

Grafica Computazionale

Grafica Computazionale Grafica Computazionale La Pipeline Grafica Fabio Ganovelli fabio.ganovelli@gmail.com a.a. 2006-2007 noi siamo qui Informazione mondo reale (es: 3D scans) creazione (es: videogames) Calcolo (es: Sci-Vis)

Dettagli

UNITÀ DI GOVERNO. Architettura funzionale. Ambiente di programmazione. Architettura hardware

UNITÀ DI GOVERNO. Architettura funzionale. Ambiente di programmazione. Architettura hardware UNITÀ DI GOVERNO Architettura funzionale Ambiente di programmazione Architettura hardware ARCHITETTURA FUNZIONALE Unità di governo abilità di muovere oggetti fisici nell ambiente di lavoro, (capacità di

Dettagli

2. Cenni di sistemi operativi

2. Cenni di sistemi operativi 2. Cenni di sistemi operativi Andrea Marongiu (andrea.marongiu@unimore.it) Paolo Valente Contiene slides dal corso «Sistemi Operativi» dei prof. Gherardi/Scandurra dell Università degli studi di Bergamo

Dettagli

Introduzione al Real Time Shading

Introduzione al Real Time Shading Introduzione al Real Time Shading Ing. Davide Vercelli, PERCRO davide.vercelli@sssup.it Sommario dal software all'hardware e ritorno cenni storici la pipeline grafica com'era: la pipeline fissa com'è:

Dettagli

Linguaggi di programmazione e astrazione

Linguaggi di programmazione e astrazione Linguaggi di programmazione e astrazione i linguaggi di programmazione ad alto livello moderni sono il più potente strumento di astrazione messo a disposizione dei programmatori che possono, con un solo

Dettagli

Sistemi a microprocessore

Sistemi a microprocessore Sistemi a microprocessore Programma: Segnali analogici e digitali Uso di segnali digitali per la rappresentazione dei numeri interi La memoria e la CPU I programmi in linguaggio macchina La connessione

Dettagli

LINGUAGGI DI ALTO LIVELLO

LINGUAGGI DI ALTO LIVELLO LINGUAGGI DI ALTO LIVELLO Si basano su una macchina virtuale le cui mosse non sono quelle della macchina hardware Linguaggi di alto livello Barriera di astrazione C Fortran Modula-2 Cobol Algol Basic Ada

Dettagli

in termini informali: un algoritmo è una sequenza ordinata di operazioni che risolve un problema specifico

in termini informali: un algoritmo è una sequenza ordinata di operazioni che risolve un problema specifico Click to edit Algoritmo Master title style algoritmo: un insieme ordinato di operazioni non ambigue ed effettivamente computabili che, quando eseguito, produce un risultato e si arresta in un tempo finito

Dettagli

Corso di Laurea Ingegneria Civile Fondamenti di Informatica. Dispensa 07. Oggetti e Java. Marzo Programmazione Java 1

Corso di Laurea Ingegneria Civile Fondamenti di Informatica. Dispensa 07. Oggetti e Java. Marzo Programmazione Java 1 Corso di Laurea Ingegneria Civile Fondamenti di Informatica Dispensa 07 Oggetti e Java Marzo 2010 Programmazione Java 1 Contenuti Il linguaggio Java Applicazioni Java e il metodo main Esempi di applicazioni

Dettagli

Macchine astratte, linguaggi, interpretazione, compilazione

Macchine astratte, linguaggi, interpretazione, compilazione Macchine astratte, linguaggi, interpretazione, compilazione 1 Macchine astratte una collezione di strutture dati ed algoritmi in grado di memorizzare ed eseguire programmi componenti della macchina astratta

Dettagli

Macchine astratte, linguaggi, interpretazione, compilazione

Macchine astratte, linguaggi, interpretazione, compilazione Macchine astratte, linguaggi, interpretazione, compilazione 1 Macchine astratte una collezione di strutture dati ed algoritmi in grado di memorizzare ed eseguire programmi componenti della macchina astratta

Dettagli

Linguaggi di Programmazione

Linguaggi di Programmazione Linguaggi di Programmazione Linguaggi di Programmazione Programmazione. Insieme delle attività e tecniche svolte per creare un programma (codice sorgente) da far eseguire ad un computer. Che lingua comprende

Dettagli

Concetti Introduttivi

Concetti Introduttivi Concetti Introduttivi Linguaggi di Programmazione http://www.dia.uniroma3.it/~roselli/ roselli@dia.uniroma3.it Credits Materiale a cura del Prof. Franco Milicchio Linguaggi di Programmazione Impartiscono

Dettagli

Scheme: struttura del programma e campo di azione

Scheme: struttura del programma e campo di azione «a2» 2013.11.11 --- Copyright Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net Scheme: struttura del programma e campo di azione Definizione e campo di azione...........................

Dettagli

Pag. 1. La formalizzazione dell informazione: Dati e Diagrammi di Flusso. Codifica degli algoritmi

Pag. 1. La formalizzazione dell informazione: Dati e Diagrammi di Flusso. Codifica degli algoritmi 1 Università degli studi di Parma Dipartimento di Ingegneria dell Informazione Informatica a.a. 2012/13 Informatica Facoltà di Medicina Veterinaria a.a. 2012/13 prof. Stefano Cagnoni La formalizzazione

Dettagli

Assegnazione di una variabile

Assegnazione di una variabile Assegnazione di una variabile Per scrivere un valore dentro una variabile si usa l operatore di assegnazione, che è rappresentato dal simbolo =. Quindi, se scrivo int a; a = 12; assegno alla variabile

Dettagli

Architetture data-flow

Architetture data-flow Architetture data-flow Le architetture che abbiamo visto finora sono dette architetture control flow. Ciò sta ad indicare che il flusso dell elaborazione è dettato dall ordine con cui le varie istruzioni

Dettagli

Introduzione alla programmazione orientata agli oggetti (prima parte) Rel 1.0

Introduzione alla programmazione orientata agli oggetti (prima parte) Rel 1.0 Introduzione alla programmazione orientata agli oggetti (prima parte) Rel 10 a cura del prof Francesco Tappi Il paradigma orientato agli oggetti implica lo sviluppo di unità di programmazione attive, chiamate

Dettagli

Introduzione al Calcolo Scientifico

Introduzione al Calcolo Scientifico Introduzione al Calcolo Scientifico Francesca Mazzia Dipartimento di Matematica Università di Bari Francesca Mazzia (Univ. Bari) Introduzione al Calcolo Scientifico 1 / 14 Calcolo Scientifico Insieme degli

Dettagli

Introduzione alla programmazione

Introduzione alla programmazione Introduzione alla programmazione Risolvere un problema Per risolvere un problema si procede innanzitutto all individuazione Delle informazioni, dei dati noti Dei risultati desiderati Il secondo passo consiste

Dettagli

SISTEMI INFORMATIVI GEOGRAFICI (GIS)

SISTEMI INFORMATIVI GEOGRAFICI (GIS) SISTEMI INFORMATIVI GEOGRAFICI (GIS) Prof. Dipartimento di Elettronica e Informazione Politecnico di Milano SISTEMA INFORMATIVO GEOGRAFICO E UN SISTEMA CHE USA SIA DATI SPAZIALI (CIOE BASATI SU RIFERIMENTI

Dettagli

La Retta Ogni funzione di primo grado rappresenta, graficamente, una retta. L equazione della retta può essere scritta in due modi

La Retta Ogni funzione di primo grado rappresenta, graficamente, una retta. L equazione della retta può essere scritta in due modi La Retta Ogni funzione di primo grado rappresenta, graficamente, una retta. L equazione della retta può essere scritta in due modi Forma implicita Forma esplicita a x b y c 0 y m x q a c y x b b Esempio

Dettagli

Il concetto di calcolatore e di algoritmo

Il concetto di calcolatore e di algoritmo Il concetto di calcolatore e di algoritmo Elementi di Informatica e Programmazione Percorso di Preparazione agli Studi di Ingegneria Università degli Studi di Brescia Docente: Massimiliano Giacomin Informatica

Dettagli

Javascript: il browser

Javascript: il browser Javascript: il browser Un browser è un programma Un browser interpreta il linguaggio HTML e visualizza le pagine sullo schermo Per la visualizzazione delle pagine un browser si appoggia al software di

Dettagli

Corso di Linguaggi di Programmazione + Laboratorio

Corso di Linguaggi di Programmazione + Laboratorio Corso di inguaggi di Programmazione + aboratorio Capitolo 1 - Introduzione Si ringrazia il Dott. Marco de Gemmis per la collaborazione nella predisposizione del materiale didattico Apprendimento di un

Dettagli

Programmazione C Massimo Callisto De Donato

Programmazione C Massimo Callisto De Donato Università degli studi di Camerino Scuola di scienze e tecnologia - Sezione Informatica Programmazione C Massimo Callisto De Donato massimo.callisto@unicam.it www.cs.unicam.it/massimo.callisto LEZIONE

Dettagli

Indice PARTE A. Prefazione Gli Autori Ringraziamenti dell Editore La storia del C. Capitolo 1 Computer 1. Capitolo 2 Sistemi operativi 21 XVII XXIX

Indice PARTE A. Prefazione Gli Autori Ringraziamenti dell Editore La storia del C. Capitolo 1 Computer 1. Capitolo 2 Sistemi operativi 21 XVII XXIX Indice Prefazione Gli Autori Ringraziamenti dell Editore La storia del C XVII XXIX XXXI XXXIII PARTE A Capitolo 1 Computer 1 1.1 Hardware e software 2 1.2 Processore 3 1.3 Memorie 5 1.4 Periferiche di

Dettagli

Corso di Fondamenti di Informatica Università degli Studi di Cassino

Corso di Fondamenti di Informatica Università degli Studi di Cassino Un linguaggio ad alto livello deve offrire degli strumenti per: rappresentare le informazioni di interesse dell algoritmo definire le istruzioni che costituiscono l algoritmo Cominciamo ad analizzare la

Dettagli

Architettura hardware

Architettura hardware Architettura hardware la parte che si può prendere a calci Architettura dell elaboratore Sistema composto da un numero elevato di componenti, in cui ogni componente svolge una sua funzione elaborazione

Dettagli

Prof. Pagani Corrado INTRODUZIONE AL LINGUAGGIO C

Prof. Pagani Corrado INTRODUZIONE AL LINGUAGGIO C Prof. Pagani Corrado INTRODUZIONE AL LINGUAGGIO C IL LINGUAGGIO C Nel 1972 Dennis Ritchie nei Bell Laboratories progettò il linguaggio C Il linguaggio possiede tutti i costrutti di controllo dei linguaggi

Dettagli

Spazio di indirizzamento virtuale

Spazio di indirizzamento virtuale Programmazione M-Z Ingegneria e Scienze Informatiche - Cesena A.A. 016-01 Spazio di indirizzamento virtuale Pietro Di Lena - pietro.dilena@unibo.it // The function name says it all int stack_overflow (){

Dettagli

Evoluzione del FORTRAN 14/03/2016. LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 15 IL LINGUAGGIO FORTRAN

Evoluzione del FORTRAN 14/03/2016. LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 15 IL LINGUAGGIO FORTRAN LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 15 IL LINGUAGGIO FORTRAN Marco Lapegna Dipartimento di Matematica e Applicazioni Universita degli Studi di Napoli Federico II wpage.unina.it/lapegna

Dettagli

Capitolo 6. Linguaggi di Programmazione. Mauro Giacomini Pearson Addison-Wesley. All rights reserved

Capitolo 6. Linguaggi di Programmazione. Mauro Giacomini Pearson Addison-Wesley. All rights reserved Capitolo 6 Linguaggi di Programmazione Mauro Giacomini 2007 Pearson Addison-Wesley. All rights reserved Capitolo 6: Linguaggi di programmazione 6.1 Prospettiva storica 6.2 Concetti della programmazione

Dettagli

Java: un linguaggio per applicazioni di rete

Java: un linguaggio per applicazioni di rete Java: un linguaggio per applicazioni di rete Moreno Falaschi Dipartimento di Ingegneria dell Informazione e Scienze Matematiche Università di Siena March 3, 2014 1 Caratteristiche di Java (SUN) Linguaggio

Dettagli

Il computer. Il case e l'unità di elaborazione. Il computer, una macchina aggiornabile.

Il computer. Il case e l'unità di elaborazione. Il computer, una macchina aggiornabile. Liceo Scientifico Vinci PROGRAMMA FINALE A.S. 2016/2017 Materia: INFORMATICA Classe 1Q Prof. Dardanelli Francesco PROGRAMMA SVOLTO NELL ANNO SCOLASTICO IL COMPUTER. Hardware e software. Il computer. Il

Dettagli

Grafica al calcolatore - Computer Graphics

Grafica al calcolatore - Computer Graphics Grafica al calcolatore - Computer Graphics 9 Tecniche di Mapping 24/11/12 Grafica 2013-9 1 Introduzione Il modello di illuminazione di Phong è abbastanza versatile: con una scelta opportuna dei vari parametri

Dettagli

Computer Graphics. Superfici di suddivisione. Modo molto diffuso per costruire mesh. modelling: superfici di suddivisione

Computer Graphics. Superfici di suddivisione. Modo molto diffuso per costruire mesh. modelling: superfici di suddivisione modelling: superfici di suddivisione Modo molto diffuso per costruire mesh 1: fare mesh di controllo a bassa risoluzione "a mano" : raffinarla automaticamente iterativamente (ad ogni interazione si aggiungono

Dettagli

Il Sistema Operativo

Il Sistema Operativo Corso di Alfabetizzazione Informatica 2003/2004 Il Sistema Operativo Modello di von Neumann Bus di sistema CPU Memoria Centrale Memoria di Massa Interfaccia Periferica 1 Interfaccia Periferica 2 Il computer

Dettagli

Calcolo numerico e programmazione Programmazione

Calcolo numerico e programmazione Programmazione Calcolo numerico e programmazione Programmazione Tullio Facchinetti 11 maggio 2012 14:05 http://robot.unipv.it/toolleeo La programmazione la programmazione è l insieme delle

Dettagli

Problema: dati i voti di tutti gli studenti di una classe determinare il voto medio della classe.

Problema: dati i voti di tutti gli studenti di una classe determinare il voto medio della classe. Problema: dati i voti di tutti gli studenti di una classe determinare il voto medio della classe. 1) Comprendere il problema 2) Stabilire quali sono le azioni da eseguire per risolverlo 3) Stabilire la

Dettagli

Algoritmi, Strutture Dati e Programmi. UD 2.b: Programmazione in Pascal

Algoritmi, Strutture Dati e Programmi. UD 2.b: Programmazione in Pascal Algoritmi, Strutture Dati e Programmi : Programmazione in Pascal Prof. Alberto Postiglione AA 2007-2008 Università degli Studi di Salerno Il Linguaggio di Programmazione Pascal Esistono molti linguaggi

Dettagli

IL SISTEMA OPERATIVO

IL SISTEMA OPERATIVO IL SISTEMA OPERATIVO (seconda parte) PROGRAMMI UTENTE INTERPRETE COMANDI FILE SYSTEM GESTIONE DELLE PERIFERICHE GESTIONE DELLA MEMORIA GESTIONE DEI PROCESSI (NUCLEO) HARDWARE La gestione delle periferiche

Dettagli

Il Concetto Intuitivo di Calcolatore. Esercizio. I Problemi e la loro Soluzione. (esempio)

Il Concetto Intuitivo di Calcolatore. Esercizio. I Problemi e la loro Soluzione. (esempio) Il Concetto Intuitivo di Calcolatore Elementi di Informatica e Programmazione Ingegneria Gestionale Università degli Studi di Brescia Docente: Prof. Alfonso Gerevini Variabile di uscita Classe di domande

Dettagli

Capitolo 6 Le infrastrutture SoftWare

Capitolo 6 Le infrastrutture SoftWare Capitolo 6 Le infrastrutture SoftWare Funzioni del sistema operativo Rendere utilizzabili le risorse fisiche presenti nel sistema informatico: garantire la correttezza e la precisione nell elaborazione

Dettagli

Funzioni, Stack e Visibilità delle Variabili in C

Funzioni, Stack e Visibilità delle Variabili in C Funzioni, Stack e Visibilità delle Variabili in C Laboratorio di Programmazione I Corso di Laurea in Informatica A.A. 2018/2019 Argomenti del Corso Ogni lezione consta di una spiegazione assistita da slide,

Dettagli

Funzioni, Stack e Visibilità delle Variabili in C

Funzioni, Stack e Visibilità delle Variabili in C Funzioni, Stack e Visibilità delle Variabili in C Programmazione I e Laboratorio Corso di Laurea in Informatica A.A. 2016/2017 Calendario delle lezioni Lez. 1 Lez. 2 Lez. 3 Lez. 4 Lez. 5 Lez. 6 Lez. 7

Dettagli

Corso di Tecniche Avanzate per la Grafica

Corso di Tecniche Avanzate per la Grafica Corso di Tecniche Avanzate per la Grafica GLSL Docente: Massimiliano Corsini Laurea Specialistica in Informatica Università di Ferrara Cosa sono gli shaders? Gli shaders sono programmi che vengono eseguiti

Dettagli

ITI M. FARADAY. Programmazione a. s

ITI M. FARADAY. Programmazione a. s ITI M. FARADAY Programmazione a. s. 2018-2019 Disciplina: INFORMATICA Indirizzo: INFORMATICA E TELECOMUNICAZIONI Classi: Terza A Terza B Ore settimanali previste: 6 (3 ora Teoria - 3 ore Laboratorio) Docenti:

Dettagli

Algoritmi, Strutture Dati e Programmi. UD 2.d: Linguaggi Procedurali

Algoritmi, Strutture Dati e Programmi. UD 2.d: Linguaggi Procedurali Algoritmi, Strutture Dati e Programmi : Linguaggi Procedurali Prof. Alberto Postiglione AA 2007-2008 Università degli Studi di Salerno LINGUAGGI PROCEDURALI Curtin, cap. 12.3 1 Linguaggi di Programmazione

Dettagli

Grafica al Calcolatore Tecniche di mappatura - 1. Introduzione

Grafica al Calcolatore Tecniche di mappatura - 1. Introduzione Grafica al Calcolatore Tecniche di mappatura - 1 Introduzione Grafica al Calcolatore Tecniche di mappatura - 2 Texture mapping Grafica al Calcolatore Tecniche di mappatura - 3 La texture può essere applicata

Dettagli

Tipi derivati. Strutture Matrici typedef enum

Tipi derivati. Strutture Matrici typedef enum Tipi derivati Strutture Matrici typedef enum Le strutture Una struttura, o struct, è un tipo di dato derivato che permette di aggregare un insieme di elementi, detti campi, all'interno di un unica entità

Dettagli

INFORMATICA. L informatica comprende:

INFORMATICA. L informatica comprende: Varie definizioni: INFORMATICA Scienza degli elaboratori elettronici (Computer Science) Scienza dell informazione Definizione proposta: Scienza della rappresentazione e dell elaborazione dell informazione

Dettagli

Le Funzioni in C. Fondamenti di Informatica Anno Accademico 2010/2011. Corso di Laurea in Ingegneria Civile Politecnico di Bari Sede di Foggia

Le Funzioni in C. Fondamenti di Informatica Anno Accademico 2010/2011. Corso di Laurea in Ingegneria Civile Politecnico di Bari Sede di Foggia Le Funzioni in C Corso di Laurea in Ingegneria Civile Politecnico di Bari Sede di Foggia Fondamenti di Informatica Anno Accademico 2010/2011 docente: prof. Michele Salvemini 1/24 Sommario Le funzioni Il

Dettagli

Macchine astratte, linguaggi, interpretazione, compilazione

Macchine astratte, linguaggi, interpretazione, compilazione Macchine astratte, linguaggi, interpretazione, compilazione 1 Macchine astratte una collezione di strutture dati ed algoritmi in grado di memorizzare ed eseguire programmi componenti della macchina astratta

Dettagli

PROGRAMMAZIONE DISCIPLINARE LICEO SCIENTIFICO OPZIONE SCIENZE APPLICATE INFORMATICA CLASSE TERZA

PROGRAMMAZIONE DISCIPLINARE LICEO SCIENTIFICO OPZIONE SCIENZE APPLICATE INFORMATICA CLASSE TERZA PROGRAMMAZIONE DISCIPLINARE PROGRAMMAZIONE DISCIPLINARE LICEO SCIENTIFICO OPZIONE SCIENZE APPLICATE INFORMATICA CLASSE TERZA 1. Competenze: le specifiche competenze di base disciplinari previste dalla

Dettagli

Lezione 6 Introduzione al C++ Mauro Piccolo

Lezione 6 Introduzione al C++ Mauro Piccolo Lezione 6 Introduzione al C++ Mauro Piccolo piccolo@di.unito.it Linguaggi di programmazione Un linguaggio formale disegnato per descrivere la computazione Linguaggi ad alto livello C, C++, Pascal, Java,

Dettagli

Il Concetto Intuitivo di Calcolatore. Esercizio. I Problemi e la loro Soluzione. (esempio)

Il Concetto Intuitivo di Calcolatore. Esercizio. I Problemi e la loro Soluzione. (esempio) Il Concetto Intuitivo di Calcolatore Elementi di Informatica e Programmazione Ingegneria Gestionale Università degli Studi di Brescia Docente: Prof. Alfonso Gerevini Variabile di uscita Classe di domande

Dettagli