Как полностью очистить контекст и холст в three.js - PullRequest
0 голосов
/ 23 января 2019

У нас есть приложение, которое также работает на iPad. Использование three.js r100.

У него есть «главное» и несколько «всплывающих окон», каждое из которых имеет свой собственный холст, сцену и средство визуализации. У «главного» есть сцена и т. Д., Которые всегда отображаются.

Чтобы избежать проблем с памятью, мы создаем все объекты при открытии всплывающего окна и очищаем его при закрытии. Но на iPad в вебинфо все еще отображаются списки закрытых всплывающих окон.

И после открытия / закрытия нескольких всплывающих окон мы получаем ошибку о слишком большом количестве контекстов («На этой странице слишком много активных контекстов WebGL, самый старый контекст будет утерян».). Первый потерянный контекст - это «главная» сцена. После этого система пытается потерять «всплывающий» контекст. Отображается вторая ошибка: «WebGL: INVALID_OPERATION: lostContext: контекст уже потерян». Это кажется логичным, потому что мы использовали forceContextLoss () при закрытии всплывающего окна.

При закрытии всплывающего окна мы:

  • распоряжаться всем (материалом и т. Д.) На сцене
  • распоряжаться OrbitControl
  • избавиться от рендерера
  • forceContextLoss () средство визуализации
  • удалить холст из DOM

Я подозреваю, что холст не позволяет очищать контексты, но, может быть, я что-то упустил? Итак, как мы можем полностью удалить контекст всплывающих окон?

Спасибо, Виллем

1 Ответ

0 голосов
/ 23 января 2019

Не уверен, что это прямой ответ, но, думаю, вам повезет больше либо

(а) с использованием одного контекста и теста ножниц для эмуляции нескольких полотен (рекомендуется)

См. методов, подобных этой

или

(b) с использованием виртуального контекста webgl , который имитирует несколько контекстов поверх одного контекста.

Если у вас действительно есть только 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>
...