Il costo effettivo di un particolare cambio di stato varia con così tanti fattori che una risposta generale è pressoché impossibile.
Innanzitutto, ogni cambio di stato può potenzialmente avere sia un costo sul lato CPU che un costo sul lato GPU. A seconda del driver e dell'API grafica, il costo della CPU può essere pagato interamente sul thread principale o parzialmente su un thread in background.
In secondo luogo, il costo della GPU può dipendere dalla quantità di lavoro in volo. Le GPU moderne sono molto pipeline e adorano fare un sacco di lavoro in volo contemporaneamente, e il più grande rallentamento che puoi ottenere è quello di bloccare la pipeline in modo che tutto ciò che è attualmente in volo debba ritirarsi prima che lo stato cambi. Cosa può causare uno stallo della pipeline? Bene, dipende dalla tua GPU!
La cosa che devi sapere per capire le prestazioni qui è: cosa devono fare il driver e la GPU per elaborare il cambiamento di stato? Questo ovviamente dipende dalla tua GPU e anche dai dettagli che gli ISV spesso non condividono pubblicamente. Tuttavia, ci sono alcuni principi generali .
Le GPU sono generalmente suddivise in frontend e backend. Il frontend gestisce un flusso di comandi generati dal driver, mentre il backend esegue tutto il lavoro reale. Come ho detto prima, il backend ama avere molto lavoro in volo, ma ha bisogno di alcune informazioni per memorizzare informazioni su quel lavoro (forse compilato dal frontend). Se calciate abbastanza piccoli lotti e consumate tutto il silicio per tenere traccia del lavoro, allora il frontend dovrà fermarsi anche se ci sono molti cavalli inutilizzati in giro. Quindi un principio qui: più cambiamenti di stato (e piccoli disegni), più è probabile che tu debba morire di fame il backend della GPU .
Mentre un disegno viene effettivamente elaborato, fondamentalmente stai solo eseguendo programmi shader, che stanno facendo accessi alla memoria per recuperare le tue uniformi, i dati del buffer dei vertici, le trame, ma anche le strutture di controllo che dicono alle unità shader dove si trovano i buffer dei vertici e le tue trame sono. E la GPU ha anche cache davanti a quegli accessi alla memoria. Quindi, ogni volta che lanci nuove uniformi o nuovi collegamenti texture / buffer nella GPU, probabilmente la prima volta che li leggerà subirà un errore nella cache. Un altro principio: la maggior parte dei cambiamenti di stato provocherà una mancata cache della GPU. (Ciò è più significativo quando si gestiscono da soli buffer costanti: se si mantengono gli stessi buffer costanti tra i disegni, è più probabile che rimangano nella cache sulla GPU.)
Una grande parte del costo per i cambiamenti di stato per le risorse dello shader è il lato CPU. Ogni volta che si imposta un nuovo buffer costante, è molto probabile che il driver stia copiando il contenuto di quel buffer costante in un flusso di comandi per la GPU. Se si imposta una singola uniforme, è molto probabile che il driver lo trasformi in un grande buffer costante dietro la schiena, quindi deve cercare l'offset per quella uniforme nel buffer costante, copiare il valore in, quindi contrassegnare il buffer costante sporco in modo da poter essere copiato nel flusso di comandi prima della successiva chiamata di disegno. Se si associa una nuova trama o buffer di vertici, il driver sta probabilmente copiando una struttura di controllo per quella risorsa. Inoltre, se stai usando una GPU discreta su un sistema operativo multitasking, il driver deve tenere traccia di tutte le risorse che usi e quando inizi a usarlo in modo che il kernel " s Il gestore della memoria GPU può garantire che la memoria per quella risorsa sia residente nella VRAM della GPU quando si verifica il sorteggio. Principio:i cambiamenti di stato fanno sì che il driver rimuova la memoria per generare un flusso di comandi minimo per la GPU.
Quando cambi lo shader corrente, probabilmente stai causando un errore nella cache della GPU (hanno anche una cache delle istruzioni!). In linea di principio, il lavoro della CPU dovrebbe essere limitato all'inserimento di un nuovo comando nel flusso di comandi che dice "usa lo shader". In realtà, però, c'è un sacco di compilation di shader da affrontare. Molto spesso i driver GPU compilano pigramente shader, anche se lo shader è stato creato in anticipo. Più pertinenti a questo argomento, tuttavia, alcuni stati non sono supportati in modo nativo dall'hardware della GPU e vengono invece compilati nel programma shader. Un esempio popolare sono i formati di vertici: questi possono essere compilati nello shader di vertici invece di essere uno stato separato sul chip. Quindi, se usi formati di vertici che non hai mai usato prima con uno specifico shader di vertici, ora potresti pagare un sacco di costi della CPU per patchare lo shader e copiare il programma shader nella GPU. Inoltre, il compilatore driver e shader può cospirare per fare ogni sorta di cose per ottimizzare l'esecuzione del programma shader. Ciò potrebbe significare ottimizzare il layout di memoria delle uniformi e delle strutture di controllo delle risorse in modo che siano ben inserite nella memoria adiacente o nei registri shader. Pertanto, quando si modificano gli shader, il driver può guardare tutto ciò che è già stato associato alla pipeline e reimballarlo in un formato completamente diverso per il nuovo shader, quindi copiarlo nel flusso di comandi. Principio: Ciò potrebbe significare ottimizzare il layout di memoria delle uniformi e delle strutture di controllo delle risorse in modo che siano ben inserite nella memoria adiacente o nei registri shader. Pertanto, quando si modificano gli shader, il driver può guardare tutto ciò che è già stato associato alla pipeline e reimballarlo in un formato completamente diverso per il nuovo shader, quindi copiarlo nel flusso di comandi. Principio: Ciò potrebbe significare ottimizzare il layout di memoria delle uniformi e delle strutture di controllo delle risorse in modo che siano ben inserite nella memoria adiacente o nei registri shader. Pertanto, quando si modificano gli shader, il driver può guardare tutto ciò che è già stato associato alla pipeline e reimballarlo in un formato completamente diverso per il nuovo shader, quindi copiarlo nel flusso di comandi. Principio:la modifica degli shader può causare un notevole shuffling della memoria della CPU.
Le modifiche al frame buffer sono probabilmente le più dipendenti dall'implementazione, ma sono generalmente piuttosto costose sulla GPU. La tua GPU potrebbe non essere in grado di gestire contemporaneamente più chiamate di disegno a target di rendering diversi, quindi potrebbe essere necessario bloccare la pipeline tra queste due chiamate di disegno. Potrebbe essere necessario svuotare le cache in modo che la destinazione di rendering possa essere letta in seguito. Potrebbe essere necessario risolvere il lavoro che ha rinviato durante il disegno. (È molto comune accumulare una struttura di dati separata insieme a buffer di profondità, destinazioni di rendering MSAA e altro. Potrebbe essere necessario finalizzare quando si passa da quella destinazione di rendering. Se ci si trova in una GPU basata su tile , come molte GPU mobili, potrebbe essere necessario scaricare una quantità abbastanza grande di ombreggiatura effettiva quando si passa da un frame buffer.) Principio:la modifica dei target di rendering è costosa per la GPU.
Sono sicuro che è tutto molto confuso, e sfortunatamente è difficile diventare troppo specifici perché i dettagli spesso non sono pubblici, ma spero che sia una visione semi-decente di alcune delle cose che stanno realmente accadendo quando chiami uno stato modifica della funzione nella tua API grafica preferita.