Errori
Il debugging in OpenGL non è particolarmente simpatico. In generale OpenGL ci avvisa quando si verifica un errore, e ci si potrebbe ritrovare davanti uno schermo nero senza avere minima idea del perché.
Quello che però fa OpenGL è settare delle flag interne ogni volta che un errore viene generato. Il metodo classico per la gestione degli errori è quindi quello di chiamare getError(), una funzione che restituisce un enum (intero) associato a un errore e nello stesso tempo ne resetta la flag.
Il workflow quindi è il seguente: resettare gli errori (nel caso ne siano rimasti da operazioni precedenti), chiamare la funzione interessata, controllare in un while loop se ci sono nuovi errori settati. Ecco un esempio:
// Reset
while(glGetError() != GL_NO_ERROR);
// Chiamata funzione
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices);
// Controllo errori
while((GLenum error = glGetError()) != GL_NO_ERROR) {
std::cout << 'Errore: ' << error << std::endl;
}// Reset
while (gl.getError() !== gl.NO_ERROR);
// Chiamata funzione
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices)
// Controllo errori
let error = gl.getError()
while (error !== gl.NO_ERROR) {
console.error(`Errore: ${error}`)
error = gl.getError()
}Chiaramente, svolgere tutti questi passi ad ogni chiamata di funzione, non è particolarmente piacevole. Per questo generalmente si costruiscono degli appositi wrapper (il codice è ispirato da The Cherno):
#define ASSERT(x) if(!(x)) __debugbreak(); // VSCode specific
#define glCall(x) ClearErrors();\
x;\
ASSERT(glLogCall())
static void ClearErrors() {
while (gl.getError() !== gl.NO_ERROR);
}
static void glLogCall() {
bool anyError = false;
while(GLenum error = glGetError()) {
std::cout << 'Errore: ' << error << std::endl;
anyError = true;
}
return !anyError;
}function glCall<F extends (...args: any) => any>(
gl: WebGLRenderingContext,
fn: F,
...args: Parameters<F>
): ReturnType<F> {
// Clearing errors
while (gl.getError() !== gl.NO_ERROR);
let returnValue = fn.call(gl, ...args)
// Getting errors
let error = gl.getError()
let anyError = error !== gl.NO_ERROR
while (error !== gl.NO_ERROR) {
console.error(`[WebGL Error] ${error}`)
error = gl.getError()
}
if (anyError) {
debugger
throw new Error('WebGL error, stopping execution')
}
return returnValue
}e poi i wrapper si possono usare in questo modo:
glCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices));glCall(gl, gl.bindBuffer, gl.ELEMENT_ARRAY_BUFFER, indices)NOTA: nell’ultima versione di OpenGL (e non in WebGL) è presente una funzione che permette di registrare un callback da chiamare in caso di errore, in modo che non si debba fare “polling” dopo l’esecuzione di ogni funzione.