javascript - 如何在 three.js 中完全清理上下文和 Canvas

标签 javascript ipad three.js webgl mobile-safari

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

它有一个“主”和几个“弹出”,每个都有自己的 Canvas 、场景和渲染器。 “主要”也有一个始终显示的场景等。

为避免内存问题,我们在弹出窗口打开时创建所有对象,并在弹出窗口关闭时清理。 但在 iPad 上,webinfo 仍然显示关闭弹出窗口的 Canvas 。

在打开/关闭几个弹出窗口后,我们收到有关上下文过多的错误(“此页面上有太多事件的 WebGL 上下文,最旧的上下文将丢失。”)。 丢失的第一个上下文是“主要”场景。之后,系统会尝试释放“弹出”上下文。显示第二个错误:“WebGL: INVALID_OPERATION: loseContext: context already lost”。这似乎合乎逻辑,因为我们在关闭弹出窗口时执行了 forceContextLoss()。

在弹出窗口关闭时我们:

  • 处理场景中的一切( Material 等)
  • 配置OrbitControl
  • 处理渲染器
  • forceContextLoss() 渲染器
  • 从 DOM 中移除 Canvas

我怀疑 Canvas 阻止上下文被清理,但也许我错过了什么? 那么,我们如何才能完全删除弹出窗口的上下文?

谢谢,威廉

最佳答案

不确定这是一个直接的答案,但我认为你也会有更好的运气

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

参见 techniques like this

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

你真的只有 1 个上下文,其他都是虚拟的

据我所知,没有办法强制浏览器释放上下文。即使强制上下文丢失也不能保证摆脱 WebGLRenderingContext 对象,事实上它明确不会。当您遇到上下文丢失事件时,即使在恢复后您也会继续使用相同的上下文对象。

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

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

下面是创建和删除上下文的快速测试。当第 17 个上下文在 Chrome 桌面上创建时,最旧的上下文丢失了

'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>

关于javascript - 如何在 three.js 中完全清理上下文和 Canvas ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54323838/

相关文章:

objective-c - 使用 Objective-C 下载大量图像

ios - UIWebView pich to zoom 在 iPhone 5.0 模拟器上工作但在 iPhone 4.3 和 iPad 4.3、5.0 模拟器上不起作用?

javascript - Three.js:手动创建的几何体

javascript - 在面板中添加关闭图标

javascript - 在 ExpressJS 中将对象传递给 javascript

ipad - PDF Quartz 渲染质量

html - 三.js丑陋的渲染效果

javascript - 使用 Javascript 对象模仿段落标题类型

javascript - 仅在已部署代码 (mvc) 中位置 0 的 JSON 中出现意外标记 u

javascript - 将相机 W.R.T 3d 对象放置在 Three.js 中