2009-03-19 10 views
20

Questa è una domanda "difficile". Non ho trovato nulla di interessante sul web.Gestione memoria C++ per streaming di texture nei videogiochi

Sto sviluppando un modulo di gestione della memoria per la mia azienda. Sviluppiamo giochi per console next-gen (Xbox 360, PS3 e PC ... consideriamo PC una console!).

Avremo bisogno in futuro, per i nostri prossimi giochi, di gestire lo streaming di texture per mondi di gioco di grandi dimensioni che non possono essere caricati tutti nella memoria della console principale (per ora non si parla di PC).

Stiamo per eseguire lo streaming all'inizio delle mipmap ad alta risoluzione delle texture (ovvero circa il 70% delle dimensioni dei dati del mondo). Forse in futuro dovremo eseguire lo streaming anche di geometria, mipmap più piccoli, audio, ecc.

Sto sviluppando un Memory Manager per quel problema, incentrato su X360 (perché su PS3 possiamo usare la memoria host e gli associati , deframmentazione automatica di GMM).

Il problema che sto affrontando è il seguente: Abbiamo deciso di riservare una specifica Area di memoria per lo streaming di texture (ad esempio 64 Megabyte) e vogliamo gestire tutte le allocazioni e le deallocazioni in quell'area. Abbiamo assegnato l'area all'inizio dell'applicazione e l'area è fisicamente garantita per essere contigua (non solo virtualmente, perché abbiamo bisogno di memorizzare le trame lì).

Ho implementato un allocatore di deframmentazione automatica, utilizzando le maniglie anziché i puntatori. Il tempo non è un problema, il problema è la frammentazione della memoria. Nel gioco cariciamo e scarichiamo continuamente bersagli in streaming, quindi vorremmo utilizzare la quantità massima del nostro buffer (64 Megabyte).

Con questo allocatore è possibile utilizzare tutto lo spazio allocato, ma la routine di deframmentazione funziona in un tempo inaccettabile (a volte 60 millisecondi, più di un frame!) Mentre l'algoritmo non è troppo male ... ci sono fin troppi meme non valida!

Sto cercando una soluzione per risolvere questo problema. Mi piacerebbe trovare almeno un buon documento, o un post-mortem, o qualcuno che abbia affrontato il mio stesso problema.

Ora sto scegliendo tra due strategie: 1) sposta la routine di deframmentazione su un thread dedicato (valido per X360 con 6 thread hw, non adatto a PS3 con solo un thread hw ... e non dirmelo per utilizzare SPU!) con tutti i problemi di multithreading delle regioni di blocco, di accedere a una regione che viene spostata, ... 2) trovare una soluzione "incrementale" al problema della deframmentazione: possiamo assegnare a ciascun frame un budget di tempo (ad esempio a 1 millisecondo) per la deframmentazione e Memory Manager farà ciò che può fare nel budget di ogni frame.

Qualcuno può dirmi la sua esperienza?

risposta

14

Recentemente ho studiato molto riguardo alla gestione della memoria e questo è l'articolo più informativo e utile che ho trovato in rete.

http://www.ibm.com/developerworks/linux/library/l-memory/

Sulla base di tale documento il migliore e più veloce risultato si ottiene è quello di dividere i 64 MB in pezzi di uguali dimensioni. La dimensione dei pezzi dipende dalla dimensione dell'oggetto. E allocare o deallocare un intero chunk alla volta. È

  1. Più veloce della garbage collection incrementale.
  2. Più semplice.
  3. E risolve il problema di "troppa fragmantazione" di una certa quantità.

Leggilo, troverai informazioni eccellenti su ogni possibile soluzione e vantaggi e demeriti per ciascuno.

+1

Come sviluppatore di giochi I secondo questa idea, siamo fortunati che le trame di gioco hanno delle belle dimensioni, per impostazione predefinita, quindi dovrebbe funzionare bene, ma potresti anche voler utilizzare un paio di dimensioni del bucket invece di un singolo bucket. Ora un'altra ottimizzazione da considerare è quella di creare un algoritmo che posiziona le trame da utilizzare nello stesso rendering passandole l'una accanto all'altra, ma non farà una differenza enorme, forse solo ricordare e ottimizzare se davvero ne hai bisogno, dipendere dal gioco e rendering. –

+0

ottimo link, ottima risposta! Molte grazie! – ugasoft

+0

questo è uno dei migliori collegamenti che ho trovato finora. grazie mille. – pigiuz

2

Poiché si utilizzano le maniglie, si ha molta libertà di spostare la memoria. Penso che usare un thread separato non sia probabilmente il migliore (il più sicuro o il più veloce) - suppongo che staresti meglio usando un tipo di allocatore di copia incrementale, dove su ogni malloc() o free() compatti (copia avanti o indietro in memoria) un certo numero di blocchi allocati, con il numero di byte che copi depleting di un "budget" che viene periodicamente ripristinato al suo valore iniziale (ad es. su ogni aggiornamento dello schermo). (Ovviamente vengono copiati solo interi blocchi.)

L'idea è che la copia di un determinato numero di byte richiede una quantità di tempo abbastanza prevedibile, quindi è possibile stimare quanti byte di copia è possibile eseguire in modo sicuro per l'aggiornamento dello schermo e limitati a quello. Se c'è abbastanza tempo nel budget, una chiamata a malloc() o free() deframmenterà completamente la memoria, altrimenti la deframmenterà il più possibile nei limiti di tempo.

Ci sono alcune domande che sto lasciando irrisolto qui - ad es. esattamente come compattare la memoria. Un allocatore di copia non incrementale standard può solo iniziare ad allocare dal lato anteriore, quindi copiare tutto sul retro (liberando la memoria in primo piano) quando la memoria si esaurisce, ma qui non si ha questa libertà. Potresti aver bisogno di alcune euristiche per decidere se spostare i blocchi avanti o indietro. L'importante è evitare oscillazioni (lo stesso blocco viene spostato avanti e indietro nelle chiamate successive a malloc() o free()).

1

Raccomanderei un approccio incrementale. Ogni fotogramma trova un blocco contiguo di memoria inutilizzata che ha spazio libero su entrambi i lati e lo sposta in qualsiasi direzione consenta di adattarlo. oppure puoi semplicemente spostare tutti i blocchi in una direzione, trovare una fessura e un blocco inutilizzato che si adatta meglio e spostarlo. Sul 360 dovresti probabilmente usare una discussione per fare la mossa, mentre sulla PS3 sarebbe meglio usare la GPU per spostare i dati in giro per te.

5

Perché non utilizzare più aree di memoria per le trame e il pool in streaming in base alle dimensioni della trama?

Insomniac ha un documento sull'implementazione del flusso di texture su PS3. Suppongo che potrebbe essere utile: link.

Per strategie di allocazione generali per ridurre al minimo la frammentazione, può essere utile il Doug Lea.

Ma dalla mia lettura della tua domanda, sembra che tu stia pensando troppo e consiglio vivamente un approccio comune. (Anche eseguire una deframmentazione sulla memoria combinata di scrittura non sembra particolarmente sicuro o divertente.)

2

Abbiamo quasi esattamente il sistema che hai descritto, eccetto che allociamo in slot a dimensione fissa - 256x256, 512x512, 1024x1024 e Texture 2048x2048, in due formati ciascuno (DXT1 e DXT5) - precisamente per evitare la gestione della memoria.

+0

non hai trame rettangolari? abbiamo anche 512x1024, 256x512 e 256x1024 (ma abbiamo evitato le texture 2048). Questo porterà a molte "dimensioni fisse" ... – ugasoft