È essenzialmente perché non tutte le GPU possono supportare le chiamate di funzione e, anche se possono, le chiamate di funzione possono essere piuttosto lente o avere limitazioni come una profondità dello stack molto ridotta.
Il codice shader e il codice di calcolo della GPU possono sembrare avere chiamate di funzione in tutto il luogo, ma in circostanze normali sono tutte inserite al 100% dal compilatore. Il codice macchina eseguito dalla GPU contiene rami e loop, ma nessuna chiamata di funzione. Tuttavia, le chiamate di funzione ricorsive non possono essere integrate per ovvie ragioni. (A meno che alcuni degli argomenti non siano costanti tempo di compilazione, in modo tale che il compilatore possa piegarli e incorporare l'intero albero delle chiamate.)
Per implementare vere chiamate di funzione, è necessario uno stack. Il più delle volte, il codice shader non utilizza affatto uno stack: le GPU hanno file di registro di grandi dimensioni e gli shader possono mantenere tutti i loro dati nei registri per tutto il tempo. È difficile far funzionare uno stack perché (a) avresti bisogno di molto spazio nello stack per fornire tutti i numerosi orditi che possono essere in volo contemporaneamente, e (b) il sistema di memoria GPU è ottimizzato per raggruppare molto insieme delle transazioni di memoria per raggiungere un throughput elevato, ma ciò comporta spese a causa della latenza, quindi la mia ipotesi è che le operazioni dello stack come il salvataggio / ripristino delle variabili locali sarebbero terribilmente lente.
Storicamente, le chiamate di funzione a livello hardware non sono state troppo utili sulla GPU, poiché ha più senso incorporare tutto nel compilatore. Quindi gli architetti della GPU non si sono concentrati sul renderli veloci. Probabilmente potrebbero essere fatti alcuni diversi compromessi, se in futuro vi sarà una richiesta di chiamate efficienti a livello di hardware, ma (come per tutto ciò che riguarda l'ingegneria), ciò comporterà un costo altrove.
Per quanto riguarda il raytracing, il modo in cui le persone di solito gestiscono questo genere di cose è creando code di raggi che stanno per essere rintracciati. Invece di ricorrere, aggiungi un raggio a una coda e ad alto livello da qualche parte hai un ciclo che continua l'elaborazione fino a quando tutte le code sono vuote. Tuttavia, richiede una riorganizzazione significativa del codice di rendering se si parte da un classico raytracer ricorsivo. Per ulteriori informazioni, un buon documento da leggere al riguardo è Wavefront Path Tracing .