2015-02-01 5 views
7

In varie fonti ho visto raccomandazioni per i buffer "unbinding" dopo l'uso, ovvero impostandolo su null. Sono curioso di sapere se c'è davvero bisogno di questo. per esempio.Non associare un buffer WebGL, ne vale la pena?

var buffer = gl.createBuffer(); 
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 

// ... buffer related operations ... 

gl.bindBuffer(gl.ARRAY_BUFFER, null); // unbinding 

Da un lato, è probabile meglio per il debug, come probabilmente otterrete migliori messaggi di errore, ma c'è qualche perdita significativa delle prestazioni dai buffer non impegnativa per tutto il tempo? In genere è consigliabile ridurre le chiamate WebGL laddove possibile.

+2

Non ho mai sciolto un buffer. – gman

risposta

9

La ragione per cui le persone spesso non accettano buffer e altri oggetti è di minimizzare gli effetti collaterali di funzioni/metodi. È un principio generale di sviluppo del software che le funzioni dovrebbero eseguire solo le operazioni pubblicizzate e non avere effetti collaterali imprevisti. Pertanto, è una pratica comune che se una funzione lega oggetti, li separa prima di tornare.

Diamo un'occhiata a un esempio tipico (senza sintassi di linguaggio particolare). In primo luogo, definiamo una funzione che crea una texture senza alcun contenuto definito:

function GLuint createEmptyTexture(int texWidth, int texHeight) { 
    GLuint texId; 
    glGenTextures(1, &texId); 
    glBindTexture(GL_TEXTURE_2D, texId); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidth, texHeight, 0, 
       GL_RGBA, GL_UNSIGNED_BYTE, 0); 
    return texId; 
} 

Quindi, diamo un'altra funzione per creare una texture. Ma questo riempie la texture con i dati da un buffer (che credo non è supportato in WebGL ancora, ma comunque aiuta illustra il principio generale):

function GLuint createTextureFromBuffer(int texWidth, int texHeight, 
             GLuint bufferId) { 
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bufferId); 
    GLuint texId; 
    glGenTextures(1, &texId); 
    glBindTexture(GL_TEXTURE_2D, texId); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidth, texHeight, 0, 
       GL_RGBA, GL_UNSIGNED_BYTE, 0); 
    return texId; 
} 

Ora, posso chiamare queste funzioni, e tutto funziona come previsto:

GLuint tex1 = createEmptyTexture(width, height); 
GLuint tex2 = createTextureFromBuffer(width, height, bufferId); 

Ma vedere cosa succede se li chiamo nell'ordine opposto:

GLuint tex1 = createTextureFromBuffer(width, height, bufferId); 
GLuint tex2 = createEmptyTexture(width, height); 

Questa volta, entrambe le trame sarà riempito con il contenuto del buffer, perché pi xel unpack buffer era ancora legato dopo la prima funzione restituita, e quindi quando veniva chiamata la seconda funzione.

Un modo per evitare ciò è di dissipare il buffer di spacchettamento dei pixel alla fine della funzione che lo lega. E per fare in modo che problemi simili non può accadere perché la trama è ancora legato, si può sciogliere che uno così:

function GLuint createTextureFromBuffer(int texWidth, int texHeight, 
             GLuint bufferId) { 
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bufferId); 
    GLuint texId; 
    glGenTextures(1, &texId); 
    glBindTexture(GL_TEXTURE_2D, texId); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texWidth, texHeight, 0, 
       GL_RGBA, GL_UNSIGNED_BYTE, 0); 
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 
    glBindTexture(GL_TEXTURE_2D, 0); 
    return texId; 
} 

Con questa implementazione, entrambe le sequenze di chiamata di utilizzo di queste due funzioni produrrà lo stesso risultato.

Ci sono altri modi per risolvere questo problema. Per esempio:

  1. Ogni funzione documenta i suoi presupposti e gli effetti collaterali, e il chiamante è responsabile di apportare le modifiche di stato necessari per soddisfare le precondizioni della funzione successiva dopo la chiamata di una funzione con effetti collaterali.
  2. Ogni funzione è completamente responsabile della configurazione di tutto lo stato. Nell'esempio sopra, ciò significherebbe che la funzione createEmptyTexture() dovrebbe separare il buffer di spacchettamento dei pixel, poiché non si basa su nessuno.

L'approccio 1 non si adatta bene e sarà doloroso mantenere in sistemi più grandi. Approccio 2 è anche insoddisfacente perché OpenGL ha un sacco di stato, e dover impostare tutto lo stato rilevante in ogni funzione sarebbe prolisso e inefficiente.

Questa è davvero una parte di una domanda più grande: come affrontare la natura basata sullo stato di OpenGL in un'architettura software modulare? I binding di buffer sono solo un esempio di stato che devi affrontare. Questo in genere non è molto difficile da gestire in piccoli programmi scritti da soli, ma è un possibile punto problematico in sistemi più grandi. Diventa peggio se i componenti provenienti da fonti diverse (ad esempio, diversi fornitori) sono misti.

Non penso che esista un unico approccio ideale in tutti gli scenari possibili. L'importante è scegliere una strategia chiaramente definita e usarla in modo coerente. Come gestire questo meglio in vari scenari è un po 'oltre la portata di una risposta qui.

Mentre i buffer non vincolanti dovrebbero essere abbastanza economici, non sono un fan delle chiamate inutili. Quindi cercherò di evitare quelle chiamate, a meno che tu non senta davvero di aver bisogno di loro per applicare una politica chiara e coerente per il software che stai scrivendo.