【问题标题】:GL_OUT_OF_MEMORY using A-frame to display stereoscopic imagesGL_OUT_OF_MEMORY 使用 A 帧显示立体图像
【发布时间】:2021-11-24 16:36:30
【问题描述】:

我创建了一个概念验证 SPA (source code / demo),它从网络服务器加载立体图像并使用 aframe-stereo-componentA-frame 以立体 3D 呈现它们。

在 Oculus 浏览器和 Firefox Reality 中的 Quest 2 上进行测试,效果很好:通过 WebXR 在沉浸式 VR 模式下查看时,每张图像都以 3D 形式显示。

但是,在滚动浏览多个图像(通常是 8 到 12 个)后,幻灯片停止工作。 在 Firefox Reality 中,这表现为应用程序只是冻结并变得无响应。

在 Oculus 浏览器中,会显示图像描述,但图像区域仍为黑色。 使用 adb 将 Quest 2 连接到 PC 并使用 Chrome DevTools 检查页面,发生这种情况时我可以看到以下输出:

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.

诚然,正在加载的图像是大型 JPS 文件(2 到 3Mb),每个图像被渲染两次:每只眼睛一次。 但是,<img> 元素用于通过将其添加到 DOM 来加载图像内容,当请求下一个图像时,将其删除并置空以使图像内容无效,因此理论上任何一个图像都应加载时间。 因此,我假设某处一定存在内存泄漏,导致在加载了许多图像后内存被填满,但我不确定它在哪里泄漏。 我无法在使用 Chrome/Firefox 的 PC 上重现该问题,但这可能是因为它的内存比 Quest 2 多。

相关source code摘录:

<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);
};

我在源代码仓库中也有raised this as an issue

【问题讨论】:

  • 请记住,JPEG 的大小在内存中会大得多;例如,单个 4K 纹理在内存中大约有 4096 x 4096 x 4 x 1.3 = 90MB——不管任何 JPEG 压缩——因为它必须被解压缩和 mipmap 才能渲染。有一些特殊的 GPU 纹理格式,如 KTX2/Basis 可以在渲染时保持压缩,但 A-Frame doesn't support these yet
  • @DonMcCurdy 我提供的 JPS 图像全尺寸约为 8k x 2k,到目前为止我还没有遇到单个图像的任何问题,但是我可能不需要提供它们以该大小,并且如果我需要,可以通过服务器端应用程序动态调整它们的大小。目前,这是一个概念验证网络应用程序,我计划在它足够强大时将其作为新功能推出到生产网站。
  • @DonMcCurdy 我认为这可能是图像大小,但正如戴夫所说,它发生在您更改图像之后(很快变慢,最后崩溃)。顺便说一句,1.3 来自哪里?
  • 1.3 的因子近似于添加的 mipmap 大小。我通常不会在移动设备或 Quest 上超过 4K,但如果您可以灵活地动态调整大小,这是一个不错的选择。

标签: three.js aframe stereoscopy


【解决方案1】:

资产管理系统 (a-assets) 旨在帮助处理 preloading 资产,而不是在运行时将它们扔进扔出。

从资产中移除 &lt;img&gt; 元素不会释放纹理(缓存在 material system 中)。

您可以访问和清除缓存,但我会尝试“手动”管理图像,

  • 通过new THREE.Texture(src) 加载图像,例如material system does

  • 应用材料

    element.getObject3D("mesh").material.map = texture;
    material.needsUpdate = true;
    
  • texture.dispose()删除旧纹理


以为纹理是too big,但我猜每个纹理都会崩溃。

【讨论】:

  • 我尝试清除三个和材质纹理缓存,但仍然内存不足。然而,通过手动加载纹理来管理图像就可以了——我能够循环 Quest 2 上的整个图像集而没有内存问题
猜你喜欢
  • 1970-01-01
  • 2022-10-14
  • 1970-01-01
  • 2020-04-29
  • 1970-01-01
  • 2021-06-24
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
相关资源
最近更新 更多