Search code examples
shaderwebgl

WebGLRenderingContext.createShader returning null


I have a webgl canvas on a page that's loaded into an iframe. On occasion, when navigating the iframe away then back to the webgl content, the call to WebGLRenderingContext.createShader() returns a null value. No errors are thrown, just null. Around 80% of the time the call works as expected and returns an appropriate vertex shader.

Reloading the content of the iframe doesn't help with the only way to get it to create a shader again is to refresh the entire browser window. Naturally this isn't viable for me as I need the parent iframe to keep it's session configuration and data.

What am I missing with regards to debugging this?

function CompileShader(gl, type, source) {
    const shader = gl.createShader(type);

    if(shader == null){
        console.error("gl.createShader returned null!");
    }
      
    // Send the source to the shader object
    gl.shaderSource(shader, source);
      
    // Compile the shader program
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, GL.COMPILE_STATUS)) {
        console.error("Could not compile shader. \n\n" + gl.getShaderInfoLog(shader));
    }
      
    return shader;
}

Edit:

Further digging shows that it's canvas/context related as @LJᛃ mentions. I've traced the behavior to when the canvas is added to another content page when the next content page is loaded. (More detail about my use case below.)

Specifically; I create the canvas in the memory of the iframe's parent but don't add it to the parent page itself. The canvas is instead used as the context for a WebXR xrSession straight from parents memory. The xrSession is shared with the child iframe when requested.

This works very well and allows the re-use of a single xrSession between individual pages. (Spinning up and down individual sessions per content page causes nightmare levels of OpenXR instability)

If however I want to mirror the XR view to the page, I then need to appendChild the canvas to the content frame's DOM, making sure to removeChild the canvas onunload. This happens again after the next content page has loaded and is ready to start. It's during the 2ns/3rd/4th/etc. loads that the shaderSource call(s) fail.

I've checked and both the canvas and contexts remain valid and in the parent's memory after the first content page is unloaded. If I skip loading the 'duplicate' shaders on subsequent content page loads, then the systems runs, but the canvas stalls out and doesn't render.

Solution

Despite the canvas being referenced in both the parent and child page's memory; and being available to the next loaded child page after the first was unloaded. The canvas never did fully stay in memory.

The issue was resolved by passing the canvas back to the parent and attaching it to the parent's body when the content page unloads.

I'd assumed that because it was still referenced and accessible in the parent page's memory, that it would still function. But an undetermined part of the canvas's context gets broken when the document it's appended to goes away if it hasn't been appended to another document.


Solution

  • Despite the canvas being referenced in both the parent and child page's memory; and being available to the next loaded child page after the first was unloaded. The canvas never did fully stay in memory.

    The issue was resolved by passing the canvas back to the parent and attaching it to the parent's body when the content page unloads.

    I'd assumed that because it was still referenced and accessible in the parent page's memory, that it would still function. But an undetermined part of the canvas's context gets broken when the document it's appended to goes away if it hasn't been appended to another document. It's not fully GC'd, but GC'd enough to cause an issue.