In che modo la trama virtuale può effettivamente essere efficiente?


27

Per riferimento, ciò a cui mi riferisco è il "nome generico" per la prima tecnica (credo) introdotta con la tecnologia MegaTexture di idTech 5 . Guarda il video qui per una rapida occhiata su come funziona.

Ultimamente ho scritto alcuni articoli e pubblicazioni ad esso correlati e ciò che non capisco è come possa essere efficace. Non richiede un ricalcolo costante delle coordinate UV dallo spazio "pagina trama globale" in coordinate trama virtuali? E come fa a frenare la maggior parte dei tentativi di raggruppare la geometria del tutto? Come può consentire uno zoom arbitrario? Ad un certo punto non richiederebbe la suddivisione dei poligoni?

C'è solo così tanto che non capisco e non sono stato in grado di trovare risorse sull'argomento facilmente accessibili.

Risposte:


20

Panoramica

Il motivo principale per Virtual Texturing (VT) o Sparse Virtual Textures , come viene talvolta chiamato, è come ottimizzazione della memoria. L'essenza della cosa è spostare nella memoria video solo i texel reali (generalizzati come pagine / riquadri) di cui potresti aver bisogno per un fotogramma renderizzato. Quindi ti consentirà di avere molti più dati di trama in memoria offline o lenta (HDD, Optical-Disk, Cloud) di quanto altrimenti si adatterebbe alla memoria video o addirittura alla memoria principale. Se capisci il concetto di memoria virtuale utilizzata dai moderni sistemi operativi, è la stessa cosa nella sua essenza (il nome non è dato per caso).

VT non richiede il ricalcolo degli UV nel senso che lo faresti per ogni frame prima di eseguire il rendering di una mesh, quindi inviare nuovamente i dati dei vertici, ma richiede un lavoro sostanziale negli shader Vertex e Fragment per eseguire la ricerca indiretta dagli UV in arrivo. In una buona implementazione, tuttavia, dovrebbe essere completamente trasparente per l'applicazione se utilizza una trama virtuale o tradizionale. In realtà, la maggior parte delle volte un'applicazione mescolerà entrambi i tipi di texture, virtuale e tradizionale.

In teoria il batch può funzionare molto bene, anche se non ho mai esaminato i dettagli di questo. Poiché i consueti criteri per il raggruppamento della geometria sono le trame, e con VT, ogni poligono nella scena può condividere la stessa trama "infinitamente grande", teoricamente, è possibile ottenere il disegno completo della scena con 1 richiamo di disegno. Ma in realtà, entrano in gioco altri fattori che lo rendono poco pratico.

Problemi con VT

Lo zoom in / out e il movimento brusco della fotocamera sono le cose più difficili da gestire in una configurazione VT. Può sembrare molto accattivante per una scena statica, ma una volta che le cose iniziano a muoversi, saranno richieste più pagine / riquadri di trama di quanto è possibile eseguire lo streaming per la memorizzazione esterna. I file asincroni IO e il threading possono aiutare, ma se si tratta di un sistema in tempo reale, come in un gioco, dovrai solo renderizzare alcuni frame con riquadri con risoluzione inferiore fino a quando arrivano quelli ad alta risoluzione, di tanto in tanto , risultante in una trama sfocata. Non ci sono proiettili d'argento qui e questo è il problema più grande con la tecnica, IMO.

Anche il Virtual Texturing non gestisce la trasparenza in modo semplice, quindi i poligoni trasparenti necessitano di un percorso di rendering tradizionale separato per loro.

Tutto sommato, VT è interessante, ma non lo consiglierei a tutti. Può funzionare bene, ma è difficile da implementare e ottimizzare, inoltre ci sono troppi casi angolari e modifiche specifiche per i miei gusti. Ma per i grandi giochi open-world o le app di visualizzazione dei dati, potrebbe essere l'unico approccio possibile per adattare tutto il contenuto all'hardware disponibile. Con molto lavoro, può essere eseguito in modo abbastanza efficiente anche su hardware limitato, come possiamo vedere nelle versioni PS3 e XBOX360 di id's Rage .

Implementazione

Sono riuscito a far funzionare VT su iOS con OpenGL-ES, fino a un certo punto. La mia implementazione non è "spedibile", ma potrei concepibilmente farlo se volessi e avessi le risorse. Puoi visualizzare il codice sorgente qui , potrebbe aiutarti a farti un'idea migliore di come i pezzi si incastrano. Ecco un video di una demo in esecuzione sul Sim iOS. Sembra molto lento perché il simulatore è terribile nell'emulare gli shader, ma funziona senza problemi su un dispositivo.

Il diagramma seguente illustra i componenti principali del sistema nella mia implementazione. Si differenzia un po 'dalla demo SVT di Sean (link down muggito), ma è più vicina in architettura a quella presentata dall'articolo Accelerating Virtual Texturing Using CUDA , che si trova nel primo libro GPU Pro (link muggito).

sistema di texturing virtuale

  • Page Filessono le trame virtuali, già tagliate in riquadri (pagine AKA) come fase di preelaborazione, quindi sono pronte per essere spostate dal disco nella memoria video ogni volta che è necessario. Un file di paging contiene anche l'intero set di mipmap, chiamate anche mipmap virtuali .

  • Page Cache Managermantiene una rappresentazione sul lato applicazione delle trame Page Tablee Page Indirection. Poiché il trasferimento di una pagina dalla memoria offline alla memoria è costoso, è necessaria una cache per evitare di ricaricare ciò che è già disponibile. Questa cache è una cache LRU ( Least recentemente utilizzata ) molto semplice . La cache è anche il componente responsabile di mantenere aggiornate le trame fisiche con la propria rappresentazione locale dei dati.

  • Il Page Providerè una coda di lavoro asincrona che prendere le pagine necessarie per una determinata visione della scena e li invia alla cache.

  • La Page Indirectiontrama è una trama con un pixel per ogni pagina / riquadro nella trama virtuale, che mapperà gli UV in arrivo sulla Page Tabletrama della cache che contiene i dati texel effettivi. Questa trama può diventare piuttosto grande, quindi deve usare un formato compatto, come RGBA 8: 8: 8: 8 o RGB 5: 6: 5.

Ma qui ci manca ancora un pezzo chiave, ed ecco come determinare quali pagine devono essere caricate dalla memoria nella cache e di conseguenza nella Page Table. È qui che passano i Feedback e gli Page Resolverinvii.

Feedback Pass è un pre-rendering della vista, con uno shader personalizzato e con una risoluzione molto più bassa, che scriverà gli ID delle pagine richieste nel framebuffer a colori. Quel colorato patchwork del cubo e della sfera sopra sono veri e propri indici di pagina codificati come un colore RGBA. Questo rendering pre-pass viene quindi letto nella memoria principale ed elaborato da Page Resolverper decodificare gli indici di pagina e generare le nuove richieste con Page Provider.

Dopo il pre-passaggio di feedback, la scena può essere riprodotta normalmente con gli shader di ricerca VT. Ma nota che non aspettiamo il completamento della richiesta di una nuova pagina, sarebbe terribile, perché bloccheremmo semplicemente il file IO sincrono. Le richieste sono asincrone e potrebbero essere o non essere pronte al momento del rendering della vista finale. Se sono pronti, dolci, ma in caso contrario, manteniamo sempre una pagina bloccata di una mipmap a bassa risoluzione nella cache come fallback, quindi abbiamo alcuni dati di trama da usare, ma sarà sfocato.

Altre risorse vale la pena dare un'occhiata

VT è ancora un argomento piuttosto caldo sulla Computer Graphics, quindi ci sono tonnellate di buon materiale disponibile, dovresti essere in grado di trovare molto di più. Se c'è qualcos'altro che posso aggiungere a questa risposta, non esitare a chiedere. Sono un po 'arrugginito sull'argomento, non ho letto molto a riguardo nell'ultimo anno, ma è sempre positivo che la memoria riveda cose :)


Ehi, grazie per l'ottima risposta. So che questo è generalmente disapprovato, ma ho vari problemi, quindi per lo più sfogliamo le cose - per avere una panoramica intuitiva degli argomenti per il futuro (temo che l'apprendimento e l'implementazione delle cose non siano al momento disponibili ) - comunque, se possibile, potresti pubblicare un esempio di pseudocodice che delinei il processo stesso, idealmente, ma non necessariamente, illustrato?
Llamageddon,

1
@Llamageddon, succede che avevo ancora un diagramma a portata di mano;) Temo che lo pseudo-codice sarà un po 'difficile da fornire, dal momento che c'è un bel po' di codice reale. Ma spero che la risposta estesa aiuti a dare un'idea generale della tecnica.
glampert

3
Vale la pena notare che la maggior parte dell'hardware moderno ora espone tabelle di pagine programmabili, eliminando la necessità di una trama di reindirizzamento. Questa è esposto attraverso ad esempio directx12 risorse riservate , che si basa su DirectX11 piastrelle risorse , o OpenGL texture sparse .
MooseBoys il

1
@Llamageddon, il pre-pass di feedback può essere eseguito a una risoluzione inferiore per risparmiare quanta più elaborazione e memoria possibile, dal momento che i pixel di una pagina si ripetono generalmente (puoi notare i quadratini colorati nella mia demo). Hai ragione sul fatto che alla fine potrebbe mancare una pagina visibile come quella, ma di solito non avrà un grande impatto visivo perché il sistema dovrebbe sempre mantenere almeno la mipmap più bassa dell'intero VT disponibile nella cache. Quel secondo documento che ho collegato ha tutti gli esempi di shader nell'appendice, puoi anche fare riferimento al repository per il mio progetto, sono simili.
glampert

1
@glampert Ahh, capisco; ciò ha senso. Tuttavia, penso che ci siano molte opzioni per la gestione dei lucidi; nel passaggio dell'ID pagina, potresti dithering (quindi l'istogramma vedrebbe tutte le pagine, a meno che non ci fosse un numero enorme di livelli trasparenti), o usi un approccio k-buffer , o anche basando semplicemente la residenza di trama trasparente su cui gli oggetti sono vicino al videocamera (anziché renderli in un passaggio di feedback).
Nathan Reed,

11

La trama virtuale è l'estremo logico degli atlanti delle trame.


Un atlante di trama è una singola trama gigante che contiene trame per singole maglie al suo interno:

Esempio di Texture Atlas

Gli atlanti delle texture sono diventati popolari a causa del fatto che la modifica delle trame provoca un flusso completo della pipeline sulla GPU. Quando si creano le mesh, gli UV vengono compressi / spostati in modo da rappresentare la "porzione" corretta dell'intero atlante delle trame.

Come menzionato da @ nathan-reed nei commenti, uno dei principali inconvenienti degli atlanti delle texture sta perdendo le modalità di avvolgimento come ripetizione, pinza, bordo, ecc. Inoltre, se le trame non hanno abbastanza bordo attorno, puoi accidentalmente campione da una trama adiacente quando si esegue il filtraggio. Questo può portare a artefatti sanguinanti.

Gli atlanti delle trame hanno una grande limitazione: le dimensioni. Le API grafiche pongono un limite leggero alla dimensione di una trama. Detto questo, la memoria grafica è solo così grande. Quindi c'è anche un limite rigido alla dimensione della trama, data dalla dimensione della tua V-ram. Le trame virtuali risolvono questo problema, prendendo in prestito concetti dalla memoria virtuale .

Le trame virtuali sfruttano il fatto che nella maggior parte delle scene vedi solo una piccola parte di tutte le trame. Quindi, solo quel sottoinsieme di trame deve essere in vram. Il resto può essere nella RAM principale o sul disco.

Ci sono alcuni modi per implementarlo, ma spiegherò l'implementazione descritta da Sean Barrett nel suo discorso sul GDC . (che consiglio vivamente di guardare)

Abbiamo tre elementi principali: la trama virtuale, la trama fisica e la tabella di ricerca.

Trama virtuale

La trama virtuale rappresenta il mega atlante teorico che avremmo se avessimo abbastanza vram per adattarci a tutto. In realtà non esiste in memoria da nessuna parte. La trama fisica rappresenta quali dati pixel abbiamo effettivamente in Vram. La tabella di ricerca è il mapping tra i due. Per comodità, suddividiamo tutti e tre gli elementi in tessere o pagine di dimensioni uguali.

La tabella di ricerca memorizza la posizione dell'angolo in alto a sinistra del riquadro nella trama fisica. Quindi, dato un UV all'intera trama virtuale, come possiamo ottenere l'UV corrispondente per la trama fisica?

Innanzitutto, dobbiamo trovare la posizione della pagina all'interno della trama fisica. Quindi abbiamo bisogno di calcolare la posizione dei raggi UV all'interno della pagina. Finalmente possiamo aggiungere questi due offset insieme per ottenere la posizione dei raggi UV all'interno della trama fisica

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


Calcolo di pageLocInPhysicalTex

Se rendiamo la tabella di ricerca delle stesse dimensioni del numero di riquadri nella trama virtuale, possiamo semplicemente campionare la tabella di ricerca con il campionamento del vicino più vicino e otterremo la posizione dell'angolo superiore sinistro della pagina all'interno della trama fisica.

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);


Calcolo inPageLocation

inPageLocation è una coordinata UV relativa alla parte superiore sinistra della pagina, piuttosto che alla parte superiore sinistra dell'intera trama.

Un modo per calcolarlo è sottrarre i raggi UV della parte superiore sinistra della pagina, quindi ridimensionarli in base alla dimensione della pagina. Tuttavia, questo è un bel po 'di matematica. Invece, possiamo sfruttare il modo in cui è rappresentato il virgola mobile IEEE. La virgola mobile IEEE memorizza la parte frazionaria di un numero mediante una serie di frazioni di base 2.

inserisci qui la descrizione dell'immagine

In questo esempio, il numero è:

number = 0 + (1/2) + (1/8) + (1/16) = 0.6875

Ora diamo un'occhiata a una versione semplificata della trama virtuale:

Trama virtuale semplice

Il 1/2 bit ci dice se siamo nella metà sinistra della trama o a destra. Il 1/4 bit ci dice in quale quarto della metà ci troviamo. In questo esempio, poiché la trama è divisa in 16, o 4 su un lato, questi primi due bit ci dicono in quale pagina siamo. i bit ci dicono la posizione all'interno della pagina.

Possiamo ottenere i bit rimanenti spostando il float con exp2 () e rimuovendoli con fract ()

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);

Dove numTiles è un int2 che indica il numero di tessere per lato della trama. Nel nostro esempio, questo sarebbe (4, 4)

Quindi calcoliamo inPageLocation per il punto verde, (x, y) = (0.6875, 0.375)

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);

Un'ultima cosa da fare prima che abbiamo finito. Attualmente, inPageLocation è una coordinata UV nello 'spazio' di trama virtuale. Tuttavia, vogliamo una coordinata UV nello "spazio" della trama fisica. Per fare ciò non ci resta che ridimensionare inPageLocation in base al rapporto tra dimensione della trama virtuale e dimensione della trama fisica

inPageLocation *= physicalTextureSize / virtualTextureSize;



Quindi la funzione finita è:

float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
    float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);

    float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
    inPageLocation = fract(inPageLocation);
    inPageLocation *= physicalTexSize / virtualTexSize;

    return pageLocInPhysicalTex + inPageLocation;
}

Non lo sono, mi riferisco al texturing virtuale, più noto come tecnologia MegaTexture di idTech 5 . Vedi anche questo e questo . L'ho visto menzionato in una panoramica delle condutture di rendering di molti motori moderni e in alcuni articoli che usano un approccio simile per le mappe d'ombra. Ha molto in comune con gli atlanti delle trame, sì, li usa in un certo senso, ma non lo confondo con gli atlanti delle trame.
Llamageddon,

Ahh. Grazie per i collegamenti. Puoi aggiungerli alla domanda. Aggiornerò la mia risposta di conseguenza
RichieSams il

3
IMO, il principale svantaggio di atlanti a trama semplice (non trame virtuali) è che perdi le modalità di avvolgimento come ripetizione e blocco e il sanguinamento si verifica a causa del filtro / mipmapping, non della precisione in virgola mobile. Sarei sorpreso di vedere la precisione del galleggiante diventare un problema per le trame ordinarie (non virtuali); anche una trama 16K (il massimo consentito dalle API attuali) non è abbastanza grande da mettere a dura prova la precisione del galleggiante.
Nathan Reed,

@RichieSams A proposito, penso che la tua risposta sia buona, anche se a una domanda diversa. Dovresti scrivere un post di domande e risposte.
Llamageddon,

Hmm, questo lo spiega abbastanza bene, anche se non capisco davvero come funzioni con i livelli mip. Vorrei poter scrivere il mio problema specifico nel comprenderlo, ma in un certo senso mi sfugge ...
Llamageddon,
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.