Disegna in modo efficiente enormi quantità di tessere in Monogame (XNA)


8

Sto ponendo questa domanda perché non ho trovato una risposta definitiva ad essa.

Consentitemi innanzitutto di dire alcune cose sul gioco e su ciò che ho già fatto. Il gioco sarà un set RTS in un mondo generato proceduralmente usando il rumore Simplex. Il mondo è composto da blocchi di dimensioni 16 x 16 fatti di sprite di 64 x 64. Sono riuscito a caricare e scaricare blocchi in modo dinamico, il che funziona bene. Il mondo assomiglierà un po 'a Rimworld, quindi dall'alto verso il basso con diversi strati di folletti (terreno prima, folletti di transizione, alberi, decalcomanie ecc.). I mondi appena generati possono contenere entità che possono influenzare l'ambiente (ad es. Un villaggio che è diventato una città) e quindi il pezzo. Sono sicuro che questo può essere calcolato usando una sorta di funzione, ma è qualcosa da prendere in considerazione.

Il problema principale che ho è quando eseguo lo zoom indietro, vengono disegnate sempre più tessere che influiscono gravemente sulle prestazioni. A circa 30000 sprite, la sezione di disegno richiede 8 ms, che è la metà di ciò che è necessario per funzionare a 60 FPS. E questo è solo il terreno. Sto usando atlanti di texture per limitare i conteggi dei sorteggi (30000 sprite disegnati in 6 conteggi).

L' obiettivo è riuscire a rimpicciolire dal livello di città / villaggio / città fino a poter vedere un intero paese. Questo deve essere fatto in modo dinamico (ad es. Non facendo clic sull'icona di una minimappa, ma semplicemente scorrere indietro come in Supreme Commander).

Ho letto molti articoli su questo problema, ma non ho trovato o visto un chiaro esempio di dove funzionerebbe. Ecco un elenco di tecniche che ho scoperto che dovrebbero funzionare:

  • Rettangoli sporchi, come descritto qui , dove si disegna solo roba nuova e si tiene il resto nel backbuffer. Questo ha molto senso, ma non ho idea di come implementarlo in Monogame.
  • La mia scelta preferita: fare ampio uso di RenderTarget, come descritto qui , dove si disegna su un RenderTarget e quindi lo si salva come trama. Nel mio caso un pezzo 16 x 16 composto da 64 x 64 creerebbe una trama 1024 x 1024. Dubito davvero che funzionerà in termini di prestazioni, ma il risultato consisterebbe in trame molto dettagliate ed è anche ottimo da usare considerando il fatto che la maggior parte è statica (terreno / alberi ecc.) E non cambia molto. Ma questo significa anche che ogni volta che viene apportata una modifica a un blocco, Texture2D deve essere modificato utilizzando SetData, che da quello che ho sperimentato richiede una notevole quantità di CPU. Tuttavia, se le trame fossero 16 x 16, potrebbe effettivamente funzionare e ridurrebbe anche la memoria e l'utilizzo del disco.
  • Finisci le trame specificando SourceRectangle in SpriteBatch. Per grandi praterie / oceani questo è un vantaggio, poiché viene disegnato solo uno sprite. Tuttavia, per terreni dettagliati con colori e sprite diversi mescolati insieme (biomi e transizioni di biomi) temo che non farà una grande differenza.
  • La mia soluzione, che compromette i dettagli, era quella di utilizzare una tessera bianca standard 64 x 64, dargli il colore delle 4 tessere circostanti e quindi ridimensionarla in modo da coprire le 4 tessere precedenti. La differenza qui (oltre alla piastrella che ha un colore semplice) è che il paesaggio è visibilmente cambiato. Vorrei anche ricordare che le foreste devono ancora essere disegnate individualmente poiché non sono perfettamente quadrate.

Se qualcuno ha qualche idea su come affrontare questo problema, anche se ciò significa compromettere determinate cose (come i dettagli o l'utilizzo di 16 x 16 sprite), mi piacerebbe ascoltarlo.


Non lasciare che l'utente riduca così tanto lo zoom
Bálint,

Ho anche riscontrato problemi di prestazioni con il mio progetto MonoGame generato proceduralmente. La mia soluzione era quella di limitare ciò che è stato reso (limitare l'intervallo di zoom out e disegnare solo ciò che è sullo schermo). Sembra che MonoGame sia progettato con un mantra "disegna tutto su ogni fotogramma", ma spero che qualcuno più esperto con MonoGame mi corregga.
Fallimento logico

Come si rendono le piastrelle? Più specificamente, usi SpriteBatch e come lo usi?
Loodakrawa,

Risposte:


3

Le schede grafiche sono ottimizzate per disegnare alcune cose molte volte piuttosto che il contrario.

Nel tuo caso, hai molte cose (trame diverse) che vuoi disegnare molte volte (numero di tessere).

IMO, le ottimizzazioni più semplici che puoi fare per ottenere significativi miglioramenti delle prestazioni sono:

  1. Combina la tua trama in atlanti. Questo dovrebbe accelerare significativamente le cose poiché la tua scheda grafica ora sta disegnando solo una (o un paio) di trame anziché molte piccole.
  2. Aggancia le tue tessere con SpriteBatch in SpriteSortMode. Modalità modalità. Questo ordinerà i tuoi sprite per trama e minimizzerà il numero di interruttori di trama al numero effettivo di diverse trame. L'aspetto negativo di questo approccio è che i tuoi sprite non verranno ordinati in profondità. Il che presumibilmente va bene dato che le tue piastrelle sono tutte alla stessa profondità. Presumo che tu abbia anche altre cose, quindi ha senso separare uno SpriteBatch per le tessere e un altro per tutto il resto (impostato su SpriteSortMode.BackToFront).

Consiglierei di fare entrambe le cose. In bocca al lupo.


2

Ho scoperto che SpriteBatch non è adatto per disegnare piastrelle. Uno dei motivi è che gli sprite sono pensati per oggetti dinamici e sono usati per molteplici scopi. Un altro motivo è che è incredibilmente associato alla CPU , ad es. come spiegato nella descrizione, il disegno di 30.000 oggetti costa 8 ms (mentre il mio chipset ha raggiunto il massimo al 30%). Inoltre, è possibile disegnare fino a 3000 sprite prima di effettuare una nuova chiamata di estrazione alla GPU (in batch e tutte).

La soluzione è stata quella di disegnare le mie tessere usando quadratini testurizzati . Questo in combinazione con un VertexBuffer mi ha permesso di disegnare fino a 500.000 tessere strutturate in una chiamata di disegno in cui il metodo di disegno consumava all'incirca 1/20 di millisecondo. Ovviamente probabilmente non dovrò disegnarne così tanti, ma in entrambi i casi ho finalmente ottenuto che la mia GPU avesse un carico di lavoro del 75% a 60 fps costanti.


Suona molto bene. Mi piacerebbe saperne di più a riguardo - hai seguito dei tutorial o hai dei link con una descrizione più dettagliata di questo processo?
Loodakrawa,

2
Ho imparato la maggior parte di tutto ciò che ha menzionato XNA / Monogame e quads testurizzati. Tuttavia, riemers.net mi ha aiutato molto dal momento che si rivolge a persone che vogliono solo creare rapidamente qualcosa che funzioni. Ti consigliamo di seguire in modo specifico il suo tutorial sul terreno 3D poiché copre anche VertexBuffers e IndexBuffers. Trasformarlo in un tipo di terreno piastrellato non dovrebbe essere un problema.
Guguhl Pluhs

Se questo è ciò che risponde maggiormente alla tua domanda, sei più che benvenuto per contrassegnarlo come accettato :)
Vaillancourt

@GuguhlPluhs So che è tardi, ma hai ancora qualche informazione in più su questo argomento? Sto affrontando lo stesso problema.
Jelle,
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.