Ultra-Threaded Dispatch Processor
Questa componente hardware è stata introdotta per la prima volta nelle GPU R500 della famiglia di schede video Radeon X1000. Il suo scopo è quello di verifica che gli shader siano sempre occupati e non solo. Gli shader devono sempre lavorare ma soprattutto devono svolgere le giuste attività nella maniera più corretta, in modo da garantire l'elaborazione più veloce possibile.
Clicca per ingrandire
Il setup engine attende prima di inviare dati al core shader dalle tre code di vertex, geometria o pixel. Prima c'è un vero e proprio arbitro che determina quale tipo di dato sia meglio elaborare nello slot libero successivo. Questo arbitraggio è un processo dinamico basato sull'attività attuale dello shader, su quella precedente, sulla quantità e la tipologia di dati in coda e altri parametri ancora.
Una volta che i dati finiscono allo shader, possono succedere due cose. I dati vengono elaborati, oppure messi da parte e sospesi fino a che non ci si possa lavorare. Nello stesso momento nello shader convivono numerosi thread, di cui uno è in esecuzione mentre tutti gli altri sono sospesi. È proprio l'arbitro a decidere le priorità, ovvero quale thread svegliare e di conseguenza quali lasciare a nanna ancora per un po'.
In parallelo a un arbitraggio dei thread, deve essercene uno per le risorse. Queste limitate, quindi le priorità d'accesso alle matrici SIMD e alla cache vanno stabilite con saggezza. I thread che vogliono lavorare con una specifica risorsa, prendono il loro numerino e si mettono in coda come ai banconi dei supermercati. Per esempio, se un thread vuole lavorare su un ALU, dovrà aspettare finché l'arbitro - che in questo caso agisce da semaforo - dia il via libero per l'accesso alla risorsa, lo stesso accade quando serve prelevare una texture e così via. Demers ha commentato che "è un sistema molto complesso, più di qualsiasi altra cosa che abbiamo progettato in passato".
Come vedete qui sopra, ci sono due arbitri per ogni matrice SIMD. A ogni ciclo un thread viene sottoposto al giudizio, alternativamente, del primo e del secondo arbitro.
Ogni SIMD contiene sempre, quindi, due thread, così da poter decidere su quale lavorare. Terminato un thread, ne riceve immediatamente un alto da arbitrare e completare. Il concetto, come già riportato, è di tenere le matrici SIMD il più possibile occupate.
Il flusso passa nelle matrici SIMD a cicli alterni. Ovvero clock 1 - thread 1, clock - 2 thread 2, clock 3 - thread 1, clock 4 - thread 2, eccetera, finché ciascuno è completato. Il setup engine crea i thread e li raggruppa in blocchi di 64. I vertex assembler raggruppano a loro volta 64 oggetti e li spediscono in un singolo blocco. Lo stesso accade con scan converter (rasterizzatore) e geometry assembler.
Ci sono 8 arbitri per le operazioni ALU, arbitri per le cache di vertex e texture, così come arbitri per alcuni altri elementi non mostrati nei diagrammi. Per chiarire cosa necessiti di arbitraggio, Demer ha dichiarato che "ogni risorsa condivisa deve essere sottoposta ad arbitraggio. Quello dell'ALU coinvolge coppie di thread per massimizzare l'uso della risorsa. C'è un arbitraggio per ogni risorsa, ovvero per richieste di texture, esportazione all'esterno dello shader (per i thread completati), richieste alla cache, richieste di lettura o scrittura in memoria, richieste d'interpolazione e d'accesso allo shader. Questi sono i principali che mi vengono in mente. Il concetto chiave consiste, ancora una volta, nel dare accesso a tutte le risorse e fare in modo che queste siano sempre il più possibile impegnate. Sopra a tutto ciò opera il sequencing