Search code examples
javascriptshaderwebglp5.js

P5 catch shader compilation errors


I am making a shader editor in the web. Users can create their own shaders in a textarea and it will be applied to a webcam.

The problem is that I have no watch to catch shader compilation errors and display them to the user. They just raise an error in developer console.

My code

    try {
      newShaderObj = pFive.createShader(defaultVertShader, fragShader)
      pFive.shader(newShaderObj)
    } catch (e) {
      alert(e)
    }

The try / catch here doesn't work; I still get this error shown in console, without the catch ever running

p5.min.js:2 Darn! An error occurred compiling the fragment shader:ERROR: 0:4: 'asd' : syntax error

I'm also getting this other error

p5.min.js:2 Uncaught TypeError: Failed to execute 'useProgram' on 'WebGLRenderingContext': parameter 1 is not of type 'WebGLProgram'.

If I introduce a global window.onerror handler, I can rescue this one:

    window.onerror = (ev, source, lineno, colno, err) => {
      alert(err)
      pFive.shader(defaultValidShader)
    }

But it doesn't tell me anything about the underlying compilation error. Also, that pFive.shader.defaultValidShader doesn't work, and I have to reload the page to get anything working again.

So, what I would really like to do is validate/compile the shader before applying it, and also have a way to handle errors at runtime.


Solution

  • I figured this out by looking at the source code in p5 Shader class (in the init function)

    https://github.com/processing/p5.js/blob/64354940c5a116b1b27bbf4c6cb7f04b6c2b9f0a/src/webgl/p5.Shader.js

    and ended up with the following code:

        newShaderObj = pFive.createShader(defaultVertShader, fragShader)
        shaderError = checkShaderError(shaderObj, fragShader)
        if (shaderError) {
          alert(shaderError)
        } else {
          shaderObj = newShaderObj
          pFive.shader(shaderObj)
        }
    

    And the checkShaderError function:

      checkShaderError = (shaderObj, shaderText) => {
        gl = shaderObj._renderer.GL
        glFragShader = gl.createShader(gl.FRAGMENT_SHADER)
        gl.shaderSource(glFragShader, shaderText)
        gl.compileShader(glFragShader)
        if !gl.getShaderParameter(glFragShader, gl.COMPILE_STATUS) {
          return gl.getShaderInfoLog(glFragShader)
        }
        return null
      }