我们有一个应用程序也可以在 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 (推荐)
或
(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/