I have started to use Dart with WebGL to write some tech demos and tests, however I can't seem to figure out how to debug WebGL in Dart.
Markus Notch (the creator of Minecraft) had a livestream on twitch where he coded his game with Dart and WebGL too, however every time he messed up something he got an error log in his console from WebGL with the description of the error. For example check out 1:13:30. Sadly I couldn't find the part that does the debugging in his code.
Of course I can check for errors in my shaders with getShaderParameter()
and getProgramParameter()
and then get the error log with the appropriate getInfoLog()
method, however for regular debugging (e.g. check for INVALID_ENUM
errors) I can get the error as an integer with getError()
but can't turn it into an enum, error message or any humanly readable debug information.
I've tried to look into web_gl.dart
's source code but couldn't find anything in it that would allow for debugging.
Any help would be very much appreciated.
Edit: Based on Notch's answer Chromium (probably) automatically logs these errors. Not for me though, gl.getError()
reports error 1280
while the Chromium console is empty. :(
Edit2: Found the solution, check out my answer.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With