three.jsaframestereoscopy

GL_OUT_OF_MEMORY using A-frame to display stereoscopic images


I've created a proof-of-concept SPA (source code / demo) which loads stereoscopic images from a web server and renders them in stereoscopic 3D using the aframe-stereo-component for A-frame.

Testing this on a Quest 2 in the Oculus Browser and Firefox Reality, this works fine: each image appears in 3D when viewed in immersive VR mode via WebXR.

However, after scrolling through a number of images (usually 8 to 12), the slideshow stops working. In Firefox Reality, this manifests by the app just freezing and becoming unresponsive.

In Oculus Browser, the image description is displayed but the image area remains black. Connecting the Quest 2 to a PC with adb and inspecting the page using Chrome DevTools, I can see the following output when this occurs:

23:07:59.195 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D: 
23:07:59.195 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:07:59.195 [.WebGL-0x55930300]RENDER WARNING: texture bound to texture unit 0 is not renderable. It might be non-power-of-2 or have incompatible texture filtering (maybe)?
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D: 
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_OUT_OF_MEMORY : glTexImage2D: 
23:08:03.340 [.WebGL-0x55930300]GL ERROR :GL_INVALID_OPERATION : glGenerateMipmap: Can not generate mips
23:08:03.499 WebGL: too many errors, no more errors will be reported to the console for this context.

Admittedly the images being loaded are large JPS files (2 to 3Mb) and each image is rendered twice: once for each eye. However, an <img> element is used to load the image content by adding it to the DOM and when the next image is requested, this is removed and nulled to invalidate the image content so in theory only one image should be loaded at any one time. Therefore I'm assuming there must be a memory leak somewhere which is causing the memory to get filled up after a number of images have been loaded but I'm not sure where it's leaking. I'm not able to reproduce the issue on a PC using Chrome/Firefox but this is probably because it has more memory than the Quest 2.

Relevant source code extracts:

<a-scene id="scene" vr-mode-ui="enabled: false" >
    <a-assets id="assets"></a-assets>
    <a-plane id="left-image"
      material="repeat:0.5 1"
      scale="2 1 1"
      position="0 0 -1"
      stereo="eye:left"
    >
    </a-plane>
    <a-plane id="right-image"
      material="repeat:0.5 1; offset: 0.5 0"
      scale="2 1 1"
      position="0 0 -1"
      stereo="eye: right"
    ></a-plane>
</a-scene>
const stereoImageId = 'fullsize-image'
const _stereoImage = '#'+stereoImageId;
const leye = $('#left-image')[0];
const reye = $('#right-image')[0];
const $assets = $('#assets');

const removeStereoImage = function(){
    let $stereoImage = $(_stereoImage);
    if($stereoImage.length){
        let stereoImage = $stereoImage[0];
        stereoImage.onload = stereoImage.onerror = null;
        $stereoImage.attr('src', '').remove();
        $stereoImage = stereoImage = null;
    }
};

const unloadStereoImage = function(){
    removeStereoImage();
    leye.setAttribute("material", "src", '');
    reye.setAttribute("material", "src", '');
    setStereoImageVisibility(false);
};

const setStereoImageVisibility = function(visible){
    leye.setAttribute("visible", visible)
    reye.setAttribute("visible", visible)
};


const showImg = function(url, description){
    function onImgLoaded(){
        leye.setAttribute("material", "src", _stereoImage);
        reye.setAttribute("material", "src", _stereoImage);
        setStereoImageVisibility(true);
    }

    unloadStereoImage();

    let stereoImage = document.createElement('img');
    stereoImage.setAttribute('id', stereoImageId);
    stereoImage.setAttribute('crossorigin', "anonymous");
    stereoImage.setAttribute('src', url);
    stereoImage.onload = onImgLoaded;
    $(stereoImage).appendTo($assets);
};

I've also raised this as an issue in the source code repo


Solution

  • The asset management system (a-assets) is intended to help with preloading assets, not throwing them in and out at runtime.

    Removing the <img> element from the assets won't dispose the texture (which is cached within the material system).

    You can access and clear the cache, but I'd try managing the images 'manually',


    Thought that the textures are too big, but I guess each one would crash.