Dart WebGL Debugging Dart WebGL Debugging dart dart

Dart WebGL Debugging


Edit: I totally misunderstood the question. Ignore this answer.

For the shaders (vertex and fragment):

gl.compileShader(shader);boolean isCompiled = gl.getShaderParameter(shader, WebGL.COMPILE_STATUS);if (isCompiled == false) {  String compileLog = gl.getShaderInfoLog(shader);  // Display the compile log}

For the linking the program:

gl.linkProgram(program);bool isCompiled = gl.getProgramParameter(program, WebGL.LINK_STATUS);if (isCompiled == false) {  String linkLog = gl.getProgramInfoLog(program);  // Display the link log}

On some hardware, the shader info log and program info log will be an empty string if there was no errors, but you can't rely on this behavior.


Okay, so I actually found the answer, and it was really obvious.

Notch was right: Chromium automatically reports the WebGL errors. However, it stops logging them after a bit of a time ("WebGL: too many errors, no more errors will be reported to the console for this context.") and since I was logging stuff every frame my console quickly got filled up with my own information, pushing Chromium's logs to the very top, or even deleting them (not sure how many lines the debug console can store at max, but I remember scrolling up to the top a few times and I've never seen Chromium's report).

A quick launch and stop of the application revealed the information.
Gotta love this kind of errors. Sigh


Little late to the party here but there is a robust way to handle errors in WebGL in Dart and that is through creating a proxy object to the WebGLRenderingContext. This solution uses dart:mirrors so this should only be used during development and should be turned off on shipping code. The other reason to turn it off is because calling getError all the time can be a real drain on your performance, but this provides a relatively painless way to hunt errors during development.

class DebugRenderingContext implements WebGL.RenderingContext {  final StreamController<RenderingErrorEvent> _onErrorController;  final WebGL.RenderingContext _gl;  DebugRenderingContext(WebGL.RenderingContext gl)      : _gl = gl      , _onErrorController = new StreamController<RenderingErrorEvent>();  Stream<RenderingErrorEvent> get onError => _onErrorController.stream;  dynamic noSuchMethod(Invocation invocation) {    // Invoke the method and get the result    var mirror = reflect(_gl);    var result = mirror.delegate(invocation);    // See if there was an error    var errorCode = _gl.getError();    // Multiple errors can occur with a single call to WebGL so continue to    // loop until WebGL doesn't return an error    while (errorCode != WebGL.NO_ERROR) {      if (!_onErrorController.isPaused) {        // Query the symbol name        var methodName = MirrorSystem.getName(invocation.memberName);        // Put the error in the stream        _onErrorController.add(new RenderingErrorEvent._internal(errorCode, methodName));      }      errorCode = _gl.getError();    }    return result;  }}

So this code wraps a WebGL.RenderingContext and invokes methods on the actual context through the noSuchMethod method on the class. The noSuchMethod is passed in a mirror of the invocation which provides the method being called and the parameters. This is delegated to the actual WebGL.RenderingContext.

From there the getError value is checked and if an error is found it is put into a stream. This is just a class that gives you a nice output similar to what Khronos provides.

class RenderingErrorEvent {  /// The [WebGL] error code.  final int error;  /// The name of the method whose call resulted in the [error].  final String methodName;  RenderingErrorEvent(this.error, this.methodName);  /// Retrieves a human readable error message.  String get message {    var errorMessage;    switch (error) {      case WebGL.INVALID_ENUM:        errorMessage = 'An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.';        break;      case WebGL.INVALID_VALUE:        errorMessage = 'A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.';        break;      case WebGL.INVALID_OPERATION:        errorMessage = 'The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.';        break;      case WebGL.INVALID_FRAMEBUFFER_OPERATION:        errorMessage = 'The framebuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag.';        break;      case WebGL.OUT_OF_MEMORY:        errorMessage = 'There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.';        break;      default:        errorMessage = 'An unknown error occurred';        break;    }    return '${methodName}: ${errorMessage}';  }}

This code doesn't handle extensions but could be extended to do so. You would just need to provide a getExtension method and create the same sort of wrappers around the extensions you're concerned about.