如何在 PixiJS 中更优雅地处理 WebGL CONTEXT_LOST_WEBGL 错误?

How to handle WebGL CONTEXT_LOST_WEBGL errors more gracefully in PixiJS?

我有一个使用 PixiJS 的数据可视化库的 React 应用程序。

我偶尔会在 Chrome 中遇到令人沮丧的 CONTEXT_LOST_WEBGL 错误,这些错误迫使用户手动重新加载页面,以便(重新)呈现页面。

我不能经常或可靠地重现错误,但我知道它会发生,因为其他人告诉我应用程序偶尔会不显示任何数据。引发此错误的情况似乎非常依赖于上下文,因此很难概括——低功率图形适配器,或同时打开大量选项卡等。

如果最终用户打开了开发者工具控制台 window,则他们只会知道存在 CONTEXT_LOST_WEBGL 错误。否则,网页看起来只是一片空白。

我尝试了以下设置我的 React 应用程序以在 webglcontextlost 事件发生时重新加载 window 而无需用户手动干预:

componentDidMount() {
  ...
  window.addEventListener("webglcontextlost", (e) => { window.location.reload(); });
  ...
}

我不确定它是否正常工作,,如果 webglcontextlost 事件正在别处处理。或许我正在尝试订阅错误的事件?

否则,为了更优雅地处理这个问题,有没有办法在原始 Javascript 中或通过第三方库定期测量 WebGL 的可用内存,并使用该测量来代替重新加载页面,当可用内存达到某个可能预测即将发生的 CONTEXT_LOST_WEBGL 错误情况的任意阈值时?

is there a way in raw Javascript to periodically measure available memory for WebGL

没有,就像没有办法测量JavaScript内存

window.addEventListener("webglcontextlost", (e) => { window.location.reload(); });

错了。应该是

someCanvas.addEventListener("webglcontextlost", (e) => { window.location.reload(); });

每个 canvas 都可以单独失去其上下文。大多数浏览器一次只允许 8 到 16 个 WebGL 上下文。一旦达到限制 canvases 就开始失去他们的上下文。

至于优雅地恢复,需要做很多工作。基本上你需要重新创建所有 WebGL 资源,这意味着你需要构建你的代码,这样才有可能。将应用程序的所有状态与与 WebGL 相关的内容(或 pixi.js)分开,当您收到上下文丢失事件时,防止默认并重新创建所有 WebGL 内容

let gl;

someCanvas.addEventListener("webglcontextlost", (e) => {
  e.preventDefault();  // allows the context to be restored
});
someCanvas.addEventListener("webglcontextrestored", (e) => {
  initWebGL(gl);
});

gl = someCanvas.getContext('webgl');
initWebGL(gl);

请注意,pixi.js 本身可能会或可能不会被设计为处理上下文丢失

当 WebGL 上下文丢失时,以下代码帮助重新启动了我的 Pixijs Web 应用程序:

  addCanvasWebGLContextLossEventListener = () => {
    const canvases = document.getElementsByTagName("canvas");
    if (canvases.length === 1) {
      const canvas = canvases[0];
      canvas.addEventListener('webglcontextlost', (event) => {
        window.location.reload();
      });
    }
  }

  removeCanvasWebGLContextLossEventListener = () => {
    const canvases = document.getElementsByTagName("canvas");
    if (canvases.length === 1) {
      const canvas = canvases[0];
      canvas.removeEventListener('webglcontextlost');
    }
  }

对于具有多个 canvas 的其他应用程序,需要进行一些调整以添加其他侦听器。

以下代码帮助我模拟了上下文丢失的情况(并通过 webglcontextlost 事件从中恢复):

  simulateWebGLContextLoss = () => {
    // 
    // simulate loss of WebGL context, for the purposes
    // of improving user experience when the browser is 
    // overwhelmed
    //
    const canvases = document.getElementsByTagName("canvas");
    if (canvases.length === 1) {
      setTimeout(() => {
        const canvas = canvases[0];
        const webgl2Context = canvas.getContext("webgl2", {});
        if (webgl2Context) {
          console.log(`losing webgl2 context...`);
          webgl2Context.getExtension('WEBGL_lose_context').loseContext();
        }
        else {
          const webglContext = canvas.getContext("webgl", {});
          if (webglContext) {
            console.log(`losing webgl context...`);
            webglContext.getExtension('WEBGL_lose_context').loseContext();
          }
        }
      }, 5000);
    }
  }

对于 React 生命周期设置:

  componentDidMount() {
    setTimeout(() => { 
      this.addCanvasWebGLContextLossEventListener();
    }, 2500);
  }

  componentWillUnmount() {
    this.removeCanvasWebGLContextLossEventListener();
  }

需要超时,因为 canvas 元素在安装组件时尚不可用。出于我的目的,短的 2.5 秒计时器为事件处理程序提供了足够的时间来锁定 canvas。