没有可靠的方法可以知道浏览器支持多少上下文,即使您确实知道它明天可能会改变,或者它可能会根据浏览器运行的机器的各种条件或浏览器自身的启发式而改变,比如也许如果 vram 较低,则允许较少的上下文。或者,如果最新的上下文使用了太多资源,它会尝试通过丢失其他上下文来释放空间。
我个人的经验法则是浏览器至少支持 8 个上下文。这就是我建立我的网站的假设。
您大概可以理解为什么会有限制。 WebGL 应用程序使用大量资源。也许不是所有的,尤其是游戏可以很容易地使用 vram 的演出,并且 vram 不像普通 ram 那样容易虚拟化,特别是因为为了在浏览器本身中显示结果,结果必须以某种方式使其相同过程。因此,由于它们可能没有被虚拟化,而且由于它们可以使用如此多的资源,因此浏览器需要限制一次可以创建的资源数量,以便为用户访问的最新页面释放资源。
有很多技术可以使用单一上下文来显示整个网页上的许多内容,这些内容在您链接到的问答中涵盖或引用。
你可以这样算:
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;
}
另外请注意,WebGL 失去上下文的原点是 WebGL 无法控制操作系统。例如,在 Windows 中,如果任何应用程序在 GPU 上执行的操作花费了太长时间,那么操作系统本身将重置 GPU,这实际上会丢失所有应用程序的上下文,而不仅仅是您的浏览器。所有应用程序。浏览器无法阻止这种情况发生,因此浏览器只需将该信息传递到您的网页即可。同样在 Windows 中,您无需重新启动即可启用/禁用 GPU。这是浏览器无法控制的另一种情况,只需要告诉您上下文丢失了。