3

I am learning WebGL and I can feel that my speed is so slow because I am having a hard time debugging my code. Is there any extension or tool with help of which I can know the value of buffer, attribpointer, matrixes, etc.

I googled and learned about chrome extension spector.js but this does not work with me. I think it supposes to show me frames or context but when I click it shows nothing.



enter image description here



When I click red button after a few seconds it shows: No frames detected. Try moving the camera or implementing requestAnimationFrame.
Pravin Poudel
  • 1,433
  • 3
  • 16
  • 38

1 Answers1

3

Yes, WebGL is hard to debug and I'm not sure anything will make it a whole lot easier. Most bugs are not something a debugger can find that easily. Certain bugs like un-renderable textures or buffers on the correct size already get reported by the browser. Other bugs though are usually math bugs, logic bugs, or data bugs. For example there is no easy way to step through a WebGL shader.

In any case, if you want to use spector you need to structure your code to be spector friendly. Spector is looking for frames based on requestAnimationFrame.

So, let's take this example which is the last example from this page.

The code has a main function that looks like this


function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.querySelector("#canvas");
  var gl = canvas.getContext("webgl");
  if (!gl) {
    return;
  }


  // setup GLSL program
  var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);
  ...
}

main();

I changed it to this. I renamed main to init and made it so I pass in the gl context.

function init(gl) {
  // setup GLSL program
  var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);

  ...
}

Then I made a new main that looks like this


function main() {
  // Get A WebGL context
  /** @type {HTMLCanvasElement} */
  var canvas = document.querySelector("#canvas");
  var gl = canvas.getContext("webgl");
  if (!gl) {
    return;
  }

  const startElem = document.querySelector('button');
  startElem.addEventListener('click', start, {once: true});

  function start() {
    // run the initialization in rAF since spector only captures inside rAF events
    requestAnimationFrame(() => {
      init(gl);
    });
    // make so more frames so spector has something to look at.
    // Note: a normal webgl app would have a rAF loop: https://webglfundamentals.org/webgl/lessons/webgl-animation.html
    requestAnimationFrame(() => {});
    requestAnimationFrame(() => {});
    requestAnimationFrame(() => {});
    requestAnimationFrame(() => {});
    requestAnimationFrame(() => {});
  }
}

main();

And I added a button to my html

<button type="button">start</button>
<canvas id="canvas"></canvas>

The code is the way it is because we need to get a webgl context first or else spector will not notice the canvas (there will be nothing to select). After when turn to turn on spector, and only after that click the start button to run our code. We need to execute our code in a requestAnimationFrame because that is what spector is looking for. It only records WebGL functions between frames.

enter image description here

Whether or not it will help you find any bugs though is another matter.

note that, if you are on Mac, Safari also has a WebGL debugger built in but just like spector it's only designed for frames. It requires you to draw something each frame so this worked

  function start() {
    // I'm not sure running the init code in a rAF is important in Safari but it worked
    requestAnimationFrame(() => {
      init(gl);
    });
    // by default safari tries to capture 3 frames so let's give it some frames
    // Note: a normal webgl app would have a rAF loop: https://webglfundamentals.org/webgl/lessons/webgl-animation.html
    requestAnimationFrame(() => { gl.clear(gl.COLOR_BUFFER_BIT); });
    requestAnimationFrame(() => { gl.clear(gl.COLOR_BUFFER_BIT); });
    requestAnimationFrame(() => { gl.clear(gl.COLOR_BUFFER_BIT); });
    requestAnimationFrame(() => { gl.clear(gl.COLOR_BUFFER_BIT); });
    requestAnimationFrame(() => { gl.clear(gl.COLOR_BUFFER_BIT); });
  }

enter image description here

Another thing you can do is use a helper to call gl.getError after every WebGL function. Here's a script you can use

<script src="https://greggman.github.io/webgl-helpers/webgl-gl-error-check.js"></script>

You can either download it or you can just include it via the link above. Example (open the javascript console to see th error)

const gl = document.createElement('canvas').getContext('webgl');

gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.vertexAttribPointer(0, 1, gl.BYE, false, 0, 0);
<script src="https://greggman.github.io/webgl-helpers/webgl-gl-error-check.js"></script>
gman
  • 100,619
  • 31
  • 269
  • 393
  • Thanks for being on the internet and helping someone like me who is trying hard to learn !!! I wasted my couple of hours just to find that type of vertexattribpointer was gl.BYE rather than gl.BYTE. I read through that line multiple time but my eye couldn't catch it. It's so annoying for me that the browser didn't show me any error !!! – Pravin Poudel Jun 18 '20 at 10:52
  • That's strange, You should have gotten a `INVALID_ENUM` error – gman Jun 18 '20 at 12:01
  • No, but there was a warning but no error so I could not find where does the error come from. – Pravin Poudel Jun 18 '20 at 12:49
  • 1
    well if you use the script mentioned at the bottom it will stop where the error happened. – gman Jun 18 '20 at 12:59
  • could you please tell me why there are multiple requestAnimationFrame without callback. I couldn't understand it. Yes, thanks for creating this great stuff, this is a great tool and awesome. – Pravin Poudel Jun 18 '20 at 13:30
  • Just because some of those tools try to capture more than 1 frame so I just copied and pasted the lines to make more frames. Most webgl apps render continuously so no need for any of that except if you want to capture the first frame you still need to do the main/init thing and init in a requestAnimationFrame but you don't need the rest – gman Jun 18 '20 at 14:13
  • I wrote requestAnimationFrame() with init as call back within main but that didn't work. I called requestAnimationFrame inside init function at end to call it recursively. I am curious if that isnot an efficient way to do this. Just calling requestAnimationFrame(function(){ init(gl) }); doesnot gave required output with Spector.js but making recursive call worked. Could you please guide me on that because as you said spector only look for code inside requestAnimationFrame so i though calling just once may work. – Pravin Poudel Jun 18 '20 at 14:57
  • I did guide you. I gave you a link code with the exact instructions to get it working. Follow them. – gman Jun 18 '20 at 15:28
  • Then you changed something because it does work. So figure out what you did different or ask a new question and post your entire broken code. – gman Jun 18 '20 at 15:31
  • i don't have click button to trigger event, does that make difference on this? – Pravin Poudel Jun 18 '20 at 15:33
  • Yes, it makes a difference! It's required because spector doesn't start until you turn it on so it won't capture the first frame where all your initialization happens unless you don't initialize until after you turn it on. That's why there's a start button. So you can load the page, then turn on spector, then click start. – gman Jun 18 '20 at 15:36
  • Thank you it did work like a charm and thank you again for being kind and helping me. – Pravin Poudel Jun 19 '20 at 04:54
  • Thank you so much for the webgl error check script. It helped me pinpoint all my mistakes in a matter of minutes. – Elouarn Laine Apr 25 '21 at 00:41