如何在three.js中完全清理上下文和画布

时间:2019-01-23 09:22:12

标签: javascript ipad three.js webgl mobile-safari

我们有一个也在iPad上运行的应用程序。使用three.js r100。

它有一个“主”和几个“弹出窗口”,每个都有自己的画布,场景和渲染器。 “主要”也有场景等,总是显示。

为避免内存问题,我们在打开弹出窗口时创建所有对象,并在关闭弹出窗口时进行清理。 但是在iPad上,网络信息仍然显示关闭的弹出窗口的画布。

在打开/关闭几个弹出窗口后,我们收到有关上下文太多的错误(“此页面上的活动WebGL上下文太多,最旧的上下文将丢失。”)。 丢失的第一个上下文是“主要”场景。之后,系统尝试松开“弹出”上下文。显示第二个错误:“ WebGL:INVALID_OPERATION:lostContext:上下文已丢失”。这似乎合乎逻辑,因为我们在关闭弹出窗口时做了一个forceContextLoss()。

在弹出窗口关闭,我们:

  • 布置场景中的所有东西(材料等)
  • 配置OrbitControl
  • 配置渲染器
  • forceContextLoss()渲染器
  • 从DOM中删除画布

我怀疑画布阻止清理上下文,但是也许我错过了一些事情吗? 那么,如何才能完全删除弹出窗口的上下文?

谢谢,威廉

1 个答案:

答案 0 :(得分:0)

不确定这是否是直接答案,但是我想您也将有更好的运气

(a)使用单个上下文和剪刀测试来模拟多个画布(推荐)

请参见techniques like this

(b)使用virtual webgl context模拟单个上下文之上的多个上下文。

您实际上只有1个上下文,而其他上下文是虚拟的

AFAIK无法强制浏览器释放上下文。即使强迫上下文丢失也不能保证摆脱WebGLRenderingContext对象,实际上它显然不是。当您遇到上下文丢失事件时,即使还原后仍会使用相同的上下文对象。

因此,不能保证浏览器不会在创建第9个上下文(或任何限制)后立即删除最早的上下文。通常,唯一的保证是在创建新上下文时只有旧上下文会丢失。

是最近最少使用的上下文还是最早的上下文,或者上下文最少的资源还是没有更多引用的上下文取决于浏览器。确实,浏览器没有简单的方法知道要释放哪些上下文。

这里是创建和删除上下文的快速测试。在Chrome桌面上创建第17个上下文后,最早的上下文丢失了

'use strict';

/* global THREE */

function makeScene(canvas, color = 0x44aa88, timeout = 0) {
  
  const renderer = new THREE.WebGLRenderer({canvas: canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2;

  const scene = new THREE.Scene();

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  const material = new THREE.MeshPhongMaterial({color});

  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  let requestId;
  function render(time) {
    time *= 0.001;  // convert time to seconds

    cube.rotation.x = time;
    cube.rotation.y = time;

    renderer.render(scene, camera);

    requestId = requestAnimationFrame(render);
  }
  requestId = requestAnimationFrame(render);
  
  if (timeout) {
    setTimeout(() => {
      cancelAnimationFrame(requestId);
      canvas.parentElement.removeChild(canvas);
      // manually free all three objects that hold GPU resoucres
      geometry.dispose();
      material.dispose();
      renderer.dispose();
    }, timeout);
  }
}

makeScene(document.querySelector('#c'));

let count = 0;
setInterval(() => {
  console.log(++count);
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  makeScene(canvas, Math.random() * 0xFFFFFF | 0, 500);
}, 1000);
<canvas id="c"></canvas>
  
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>

这里是与virtual-webgl相同的测试

'use strict';

/* global THREE */

function makeScene(canvas, color = 0x44aa88, timeout = 0) {
  
  const renderer = new THREE.WebGLRenderer({canvas: canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2;

  const scene = new THREE.Scene();

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  const material = new THREE.MeshPhongMaterial({color});

  const cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  let requestId;
  function render(time) {
    time *= 0.001;  // convert time to seconds

    cube.rotation.x = time;
    cube.rotation.y = time;

    renderer.render(scene, camera);

    requestId = requestAnimationFrame(render);
  }
  requestId = requestAnimationFrame(render);
  
  if (timeout) {
    setTimeout(() => {
      cancelAnimationFrame(requestId);
      // take the canvas out of the dom
      canvas.parentElement.removeChild(canvas);
      // manually free all three objects that hold GPU resoures
      geometry.dispose();
      material.dispose();
      // hold on to the context incase the rendered forgets it
      const gl = renderer.context;
      // dispose the rendered in case it has any GPU resources
      renderer.dispose();
      // dispose the virutal context
      gl.dispose(); // added by virtual-webgl
    }, timeout);
  }
}

makeScene(document.querySelector('#c'));

let count = 0;
setInterval(() => {
  console.log(++count);
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  makeScene(canvas, Math.random() * 0xFFFFFF | 0, 500);
}, 1000);
<canvas id="c"></canvas>
<script src="https://greggman.github.io/virtual-webgl/src/virtual-webgl.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r98/three.min.js"></script>