Il modo più veloce per eseguire il rendering di linee con AA, variando lo spessore in DirectX


14

Quindi sto sviluppando alcuni DirectX, usando SharpDX sotto .NET per l'esattezza (ma le soluzioni API DirectX / C ++ sono applicabili). Sto cercando il modo più veloce per eseguire il rendering di linee in una proiezione ortogonale (ad esempio, simulando il disegno 2D per app scientifiche) utilizzando DirectX.

Ecco uno screenshot del tipo di grafici che sto cercando di rendere: inserisci qui la descrizione dell'immagine

Non è raro che questo tipo di grafici abbia linee con milioni di segmenti, di spessore variabile, con o senza antialiasing per linea (o AA a schermo intero on / off). Devo aggiornare i vertici delle linee molto frequentemente (ad es. 20 volte al secondo) e scaricare il più possibile sulla GPU.

Finora ho provato:

  1. Il rendering del software, ad esempio GDI +, in realtà non è male, ma ovviamente è pesante per la CPU
  2. API Direct2D: più lenta di GDI, in particolare con Antialiasing attivo
  3. Direct3D10 utilizza questo metodo per emulare AA usando i colori dei vertici e la tassellatura sul lato CPU. Anche lento (l'ho profilato e l'80% del tempo è impiegato nel calcolo delle posizioni dei vertici)

Per il 3o metodo sto usando Vertex Buffers per inviare una striscia triangolare alla GPU e aggiornando ogni 200 ms con nuovi vertici. Ricevo una frequenza di aggiornamento di circa 5 FPS per 100.000 segmenti di linea. Ne ho bisogno idealmente milioni!

Ora sto pensando che il modo più veloce sarebbe fare la tassellatura sulla GPU, ad esempio in Geometry Shader. Potrei inviare i vertici come un elenco di linee o impacchettare in una trama e decomprimere in un Geometry Shader per creare i quad. Oppure, basta inviare punti grezzi a un pixel shader e implementare il disegno di Bresenham Line interamente in un pixel shader. Il mio HLSL è arrugginito, shader modello 2 del 2006, quindi non conosco le cose folli che le GPU moderne possono fare.

Quindi la domanda è: - qualcuno l'ha mai fatto prima e hai qualche suggerimento da provare? - Hai qualche suggerimento per migliorare le prestazioni con l'aggiornamento rapido della geometria (ad es. Un nuovo elenco di vertici ogni 20 ms)?

AGGIORNAMENTO 21 gennaio

Da allora ho implementato il metodo (3) sopra usando gli shader Geometry usando LineStrip e Dynamic Vertex Buffer. Ora sto ricevendo 100FPS a 100k punti e 10FPS a 1.000.000 di punti. Questo è un enorme miglioramento, ma ora sono pieno e il calcolo è limitato, quindi ho pensato ad altre tecniche / idee.

  • Che dire dell'istanza hardware di una geometria del segmento di linea?
  • Che dire di Sprite Batch?
  • Che dire di altri metodi orientati (Pixel shader)?
  • Posso abbattere efficacemente la GPU o la CPU?

I tuoi commenti e suggerimenti sono molto apprezzati!


1
Che dire di AA nativo in Direct3D?
API-Beast

5
Puoi mostrare alcune curve di esempio che desideri tracciare? Citi un milione di vertici ma il tuo schermo probabilmente non ha molto più di un milione di pixel , è davvero necessaria questa quantità? Mi sembra che non avrai bisogno di quella piena densità di dati ovunque. Hai considerato LOD?
sam hocevar,

1
Il secondo approccio che hai collegato sembra buono, stai usando un buffer di vertici dinamico che aggiorni o ne crei uno nuovo ogni volta?

1
Probabilmente dovresti andare con un buffer di vertici dinamico (non molto lavoro, basta dire al tuo buffer di vertici di essere dinamico, mappare il buffer, copiare i tuoi dati, non mappare) ma se il tuo collo di bottiglia è in primo luogo la generazione di vertici, che probabilmente ha vinto mi aiuta molto adesso. Non pensare che ci sia qualcosa di folle nell'usare un GS per alleggerire il carico sulla CPU

1
Se il numero di vertici finisce per essere troppo grande per la GPU, potresti comunque provare a sottocampionare i tuoi input - non hai davvero bisogno di centinaia di milioni di segmenti di linea per una singola curva quando lo schermo sarà largo alcune migliaia di pixel al massimo.

Risposte:


16

Se hai intenzione di renderizzare Y = f(X)solo i grafici, ti suggerisco di provare il seguente metodo.

I dati della curva vengono passati come dati di trama , rendendoli persistenti e consentendo, glTexSubImage2Dad esempio, aggiornamenti parziali . Se è necessario scorrere, è possibile persino implementare un buffer circolare e aggiornare solo alcuni valori per frame. Ogni curva viene renderizzata come un quad a schermo intero e tutto il lavoro viene svolto dal pixel shader.

I contenuti delle trame monocomponente potrebbero apparire così:

+----+----+----+----+
| 12 | 10 |  5 | .. | values for curve #1
+----+----+----+----+
| 55 | 83 | 87 | .. | values for curve #2
+----+----+----+----+

Il lavoro del pixel shader è il seguente:

  • trova la coordinata X del frammento corrente nello spazio del set di dati
  • prendere ad es. i 4 punti dati più vicini che hanno dati; per esempio se il valore X è 41.3lo avrebbe scelto 40, 41, 42e 43.
  • interrogare la trama per i 4 valori Y (assicurarsi che il campionatore non esegua interpolazioni di alcun tipo)
  • converti le X,Ycoppie nello spazio dello schermo
  • calcola la distanza dal frammento corrente a ciascuno dei tre segmenti e quattro punti
  • usa la distanza come valore alfa per il frammento corrente

Potresti voler sostituire 4 con valori maggiori a seconda del potenziale livello di zoom.

Ho scritto uno shader GLSL molto veloce e sporco implementando questa funzione . Potrei aggiungere la versione HLSL in seguito, ma dovresti essere in grado di convertirla senza troppi sforzi. Di seguito è possibile vedere il risultato, con diverse dimensioni di linea e densità di dati:

curve

Un chiaro vantaggio è che la quantità di dati trasferiti è molto bassa e il numero di richiami è solo uno.


Questa è una tecnica piuttosto interessante: ho già fatto GPGPU, quindi impacchettare le trame con i dati è qualcosa che conosco, ma non qualcosa che ho preso in considerazione per questo. Qual è la dimensione più grande (ad es. Set di dati più grande che puoi caricare?). Scaricherò il tuo codice se non ti dispiace e ci giocherò - mi piacerebbe vedere lo spettacolo.
Dr. ABT

@ Dr.ABT La dimensione del set di dati è limitata solo dalla dimensione massima della trama. Non vedo perché milioni di punti non funzionerebbero dato un corretto layout dei dati. Si noti che il mio codice non è certamente di qualità di produzione, ma non esitate a contattarmi privatamente in caso di problemi.
Sam Hocevar,

Saluti @SamHocevar - Ci proverò. È passato un po 'di tempo da quando ho compilato un esempio di OpenGL! :-)
Dr. ABT

Contrassegnando come risposta, anche se non sto usando il caricamento delle trame, hai presentato un vero esempio OpenGL che potrebbe tracciare linee su un pixel shader e metterci nella giusta direzione !! :)
Dr. ABT,

4

C'era un capitolo Gemme GPU sul rendering di linee antialias: Fast Prefiltered Lines . L'idea di base è di rendere ogni segmento di linea come un quadruplo e calcolare, ad ogni pixel, una funzione gaussiana della distanza del centro del pixel dal segmento di linea.

Questo significa disegnare ogni segmento di linea nel grafico come un quad separato, ma in D3D11 potresti certamente usare uno shader di geometria e / o istanza per generare i quad, riducendo la quantità di dati da trasferire alla GPU solo ai punti dati loro stessi. Probabilmente imposterei i punti dati come StructuredBuffer affinché vengano letti dallo shader vertice / geometria, quindi eseguo una chiamata di disegno specificando il numero di segmenti da disegnare. Non ci sarebbero veri e propri buffer di vertici; lo shader di vertici userebbe semplicemente SV_VertexID o SV_InstanceID per determinare quali punti dati guardare.


Ehi grazie - sì, sono a conoscenza di quell'articolo, sfortunatamente nessun codice sorgente con esso :-( La premessa di GeoShader + FragShader sembra essere vincente per questo tipo di lavoro. Ottenere i dati nella GPU in modo efficiente sarà il parte difficile!
Dr. ABT

Ehi, @NathanReed, tornando a questo - se ho usato Geometry Shader o istanza per disegnare quad, allora Point1 / Point2 sono vertici opposti di un quad, come nel Pixel Shader posso calcolare la distanza dalla linea tra Pt1 e Pt2? Il pixel shader ha conoscenza della geometria di input?
Dr. ABT,

@ Dr.ABT I parametri della linea matematica devono essere inviati al pixel shader tramite interpolatori. Il pixel shader non ha accesso diretto alla geometria.
Nathan Reed,

Vedo, è possibile farlo inviando output da GeometryShader o instanzando? Per ulteriore credito (se sei interessato), ho aggiunto una Q qui: gamedev.stackexchange.com/questions/47831/…
Dr. ABT

@ Dr.ABT È esattamente lo stesso modo in cui le coordinate della trama, i vettori normali e tutto quel genere di cose vengono normalmente inviati al pixel shader - scrivendo gli output del vertice / geometria shader che vengono interpolati e immessi nel pixel shader.
Nathan Reed,

0

@ Dr.ABT - Ci scusiamo per questa è una domanda, non una risposta. Non ho trovato il modo di chiederlo nella risposta di Sam Hocevar sopra.

Potresti condividere maggiori dettagli su come hai finalmente implementato le linee per il tuo grafico? Ho lo stesso bisogno di un'app WPF. Grazie in anticipo per eventuali dettagli o codice che puoi condividere su questo.


come sapete non è una risposta, per favore modificatela e inseritela come commento
MephistonX

Stavo provando a farlo, ma non c'è un pulsante "Aggiungi commento" sul post originale.
ErcGeek,

Ciao ErcGeek, mi spiace non poter condividere la soluzione finale poiché queste informazioni sono di proprietà della mia azienda. Sebbene gli esempi e i suggerimenti sopra riportati ci mettano sulla strada giusta. Ti auguro il meglio!
Dr. ABT,

Non puoi pubblicare commenti prima di raggiungere una certa reputazione. E tutti questi downgrade sicuramente non ti daranno questa opportunità.
Mathias Lykkegaard Lorenzen,

Il ragazzo non dovrebbe essere penalizzato per voler conoscere il risultato finale e chiederlo nell'unico modo a sua disposizione. Ha votato a favore della risposta.
David Ching,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.