Per la cottura a vapore del buffer dei vertici, più glBufferSubData VS Orphaning?


8

Recentemente stavo imparando OpenGL. Nei giochi, è necessario aggiornare frequentemente la posizione degli oggetti di gioco, che entreranno e usciranno costantemente dallo schermo. Quindi significa che nel rendering dobbiamo aggiornare abbastanza spesso anche il buffer dei vertici.

Nel contesto di OpenGL, un modo intuitivo è usare glBufferSubData per aggiornare quelli che sono cambiati.

Ma ho anche letto online un trucco chiamato Orphaning che crea un nuovo buffer di dati e carica tutti i dati dei vertici.

Inoltre, a causa del costo delle incognite statali e dei costi di caricamento, anche glBufferSubData può costare di più.

Ecco la mia domanda,

  1. Quale metodo è migliore?
  2. Le bancarelle sono davvero importanti in questo caso?
  3. I cambiamenti di stato e i costi di caricamento sono davvero importanti in questo caso?

Grazie!


Perché vuoi ricaricare la geometria quando puoi effettivamente aggiornare solo la matrice? in altre parole, non è necessario ricaricare i vertici quando è necessario modificare solo le trasformazioni.
concept3d

@ concept3d Sì, hai ragione! Ho fatto un errore durante la scrittura delle descrizioni dei problemi. Grazie per avermelo ricordato! Ho già aggiornato le descrizioni.
YiFeng,

Risposte:


9

Questa è una domanda complessa con molti piccoli dettagli che contano davvero, le prestazioni variano in base alla piattaforma e all'applicazione. Quindi dovresti profilare possibili strozzature prima di investire in ottimizzazioni.

Detto questo, in primo luogo, suppongo che dovresti ridurre il più possibile i caricamenti e gli aggiornamenti, ad esempio utilizzare l'istanziazione.

In secondo luogo, si noti che le GPU non possono trasferire buffer e render allo stesso tempo, quindi tutti i comandi OpenGL nella coda comandi vengono elaborati in sequenza dal dispositivo. Esistono diversi modi per copiare i dati e / o renderli disponibili per essere utilizzati dalla GPU.

Esistono vari modi per trasmettere i dati alla GPU

1- glBufferDataoglBufferSubData metodo

Usare glBufferDatao glBufferSubDataè come memcpy. si passa un puntatore e l'operazione DMA potrebbe essere eseguita, ho detto che potrebbe perché la memoria potrebbe essere bloccata nella memoria della CPU e utilizzata direttamente dalla GPU senza effettivamente un trasferimento di memoria alla GPU, a seconda del flag di utilizzo (GL_STREAM). In opinione dovresti provarlo all'inizio perché è più semplice da implementare.

2- ottenere un puntatore alla memoria interna usando glMapBuffer

Se quanto sopra non è abbastanza buono da poter usare glMapBuffer, ottieni un puntatore alla memoria interna e puoi usare questo puntatore per riempire direttamente il buffer, questo è buono con le operazioni di lettura e scrittura dei file, poiché puoi mappare direttamente i dati del file nella memoria GPU anziché copiarlo prima in un buffer temporaneo. Se non si desidera mappare l'intero buffer, è possibile utilizzare glMapBufferRangequale può essere utilizzato per mappare una parte del buffer.

Un trucco è creare un buffer di grandi dimensioni, utilizzare la prima metà per il rendering e la seconda metà per l'aggiornamento.

3- Orfanotrofio del buffer

Per quanto riguarda il buffer orfano, questo può essere fatto usando glBufferData con null e gli stessi parametri che aveva. Il driver restituirà il blocco di memoria una volta non utilizzato. E verrà utilizzato dalla prossima chiamata glBufferData (non verrà allocata alcuna nuova memoria).

Tutti i metodi citati causano molte costose sincronizzazioni, anche in questo caso le GPU non possono trasferire buffer e render allo stesso tempo.

4- Unsynchronized Buffers

Il metodo più veloce (e più difficile da ottenere a destra) è usare i buffer senza sincronizzazione con cui puoi usare GL_MAP_UNSYNCHRONIZED_BITflag glMapBufferRange, il problema è che non viene eseguita alcuna sincronizzazione, quindi potremmo caricare i dati su un buffer iniziando a usare, e quindi rovinare tutto. È possibile utilizzare più buffer con bit non sincronizzati per semplificare un po 'le cose.


0

Lo faccio in un altro modo. Potrebbe esserci qualcosa che non va lì. Alcune cose pericolose nascoste.

(Uso C # + OpenTK.GameWindow)

Per istanziare l'oggetto uso un Array Buffer Object separato per l'insieme di matrici modello per ogni istanza. Nel vertice condiviso:

#version 400
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord;
layout (location = 4) in mat4 model_matrix;

uniform mat4 proj_matrix, view_matrix;

Nel mio codice C # le matrici sono memorizzate in un array float float[] mat4_array

Quindi associo l'array all'oggetto buffer dell'array:

public void bind_data_instances()
{
    GL.BindBuffer(BufferTarget.ArrayBuffer, id_InstanceBO);
    GL.BufferData(BufferTarget.ArrayBuffer, mat4_array.Length * sizeof(float), mat4_array, BufferUsageHint.DynamicDraw);
}

Ogni fotogramma le matrici del modello vengono aggiornate. Per aggiornare mat4_arraychiamo solo:

Buffer.BlockCopy(mat4_array_new, 0, mat4_array, 0, sizeof(float) * mat4_models.Length);

e rendere la scena. usando GL.DrawElementsInstanced.

Non ci sono chiamate OpenGL aggiuntive e funziona perfettamente.


Qual è lo scopo di avere la matrice del modello come attributo vertice?
Anton Duzenko,

È per l'estrazione di oggetti di istanza. Ed è più veloce di un array passato come variabile uniforme.
Dennis,
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.