浏览器对活动WebGL上下文的数量施加了限制。超过限制和browser will start to dump older contexts。我的理解是每个域都有限制,总的上限也有限制。
两个问题:
没有可靠的方法来知道浏览器支持多少个上下文,即使您确实知道它明天可能会更改,也可能会根据运行浏览器的计算机的各种条件或浏览器自身的启发式而改变,例如vram低,它允许较少的上下文。或者,如果最新的上下文使用过多的资源,则它会尝试通过丢失其他上下文来释放空间。
我个人的经验法则是浏览器至少支持8种上下文。这就是我建立自己的网站所要假设的。
您可能会理解为什么会有限制。 WebGL应用程序占用大量资源。也许不是全部,但特别是游戏可以轻松地使用vram的演出,并且不像常规ram那样轻易地对vram进行虚拟化,特别是因为要以某种方式在浏览器中显示结果,所有结果都必须使其相同处理。因此,由于它们很可能没有被虚拟化,并且由于它们可以使用太多资源,因此浏览器需要限制一次可以创建多少资源,以便为用户访问的最新页面释放资源。
[有很多技术人员可以使用单个上下文在整个网页上显示很多东西,这些东西在您链接到的问答中都涉及或引用。
您可以这样数:
const canvases = [];
let done;
function createCanvas() {
const canvas = document.createElement('canvas');
canvas.addEventListener('webglcontextlost', () => {
done = true;
console.log('num contexts:', canvases.length - 1);
});
const gl = canvas.getContext('webgl');
canvases.push(canvas);
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function main() {
while (!done) {
createCanvas();
await wait();
}
}
main();
[在我的Macbook Pro Chrome 80,Firefox 75,Safari 13.1和iPhone 11上的Safari上,全部报告了16个上下文。在Pixel 2 XL上的Android上的Chrome 81报告为8,在同一设备上的Firefox报告为9。
无论浏览器的限制似乎是Chrome中每个域的限制,还是Firefox和Safari中每个页面的限制。您可以对此进行测试here。打开2个窗口。在一个窗口中创建16个上下文,然后在另一个窗口中再创建一个。在Chrome浏览器中,一旦在第二个窗口中创建了另一个上下文,您将看到第一个窗口失去上下文。在Firefox和Safari中,每个窗口都有其自己的限制。
我还发现其他行为有很多变化。在Chrome上,我希望如果在一个窗口中创建16个上下文,在另一个窗口中创建1个上下文,则会在第一个窗口中看到一个丢失的内容,但是当我关闭第二个窗口时,我会看到在第一个窗口中恢复了一个丢失的上下文。我不。我不知道是否有什么事情触发该上下文恢复。
在Firefox中,使用上面链接的代码,当我在同一窗口中创建第17个上下文时,它就会陷入无限循环。它丢失了第一个上下文,即上下文寄存器要还原,firefox立即将其还原,然后丢失另一个上下文,重复。似乎无法在Firefox中使用。
正在尝试another example where I don't keep a reference to the canvases,这意味着它们可以被垃圾回收,我发现在Firefox中我从未遇到过上下文丢失事件,这是有道理的,因为我不再有效引用上下文,因此没有理由发送丢失的上下文事件。另一方面,Chrome浏览器仍会发送事件,从技术上讲,这也是正确的,因为我注册了该事件,因此该事件本身仍具有引用,并且如果我不想知道自己应该注销该事件。
显然,这是WebGL规范的未指定和未测试的部分。
看来,对于丢失的上下文,您真正能做的唯一一件事就是通知用户他们得到了一个,并向他们提供了一个重新开始的按钮(创建一个新按钮或刷新页面)
const groups = []; let done; function createCanvas(parent, lostCallback) { const canvas = document.createElement('canvas'); parent.appendChild(canvas); canvas.addEventListener('webglcontextlost', lostCallback); const gl = canvas.getContext('webgl'); return {canvas, gl}; } function createGroup() { const div = document.createElement('div'); const group = {div}; div.className = 'c'; document.body.appendChild(div); function restore() { div.innerHTML = ''; const {canvas, gl} = createCanvas(div, () => { done = true; group.gl = undefined; div.innerHTML = "context lost, click to restore"; div.addEventListener('click', restore, {once: true}); }); group.gl = gl; group.canvas = canvas; } restore(); groups.push(group); } const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); async function main() { while (!done) { createGroup(); await wait(); } } function render() { for (const {gl} of groups) { if (!gl) { continue; } gl.clearColor(Math.random() * 0.5 + 0.5, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT); } requestAnimationFrame(render); } requestAnimationFrame(render); main();
.c { display: inline-block; border: 1px solid black; margin: 1px; font-size: xx-small; } canvas { width: 100px; height: 10px; display: block; }