【问题标题】:Use GPU to draw on HTML5 Canvas on Google Chrome在 Google Chrome 上使用 GPU 在 HTML5 Canvas 上绘图
【发布时间】:2021-11-15 15:37:18
【问题描述】:

我正在使用画布绘制标记(在 SVG 中)数百次(有时是数千次)。画布大小为 300x300 像素,SVG 为 18x25 像素。

代码非常直接,我有一个 for 循环,我在画布上绘制标记:

   drawNewTile = (canvas, points) => {
        const drawn = {};
        const context = canvas.getContext('2d');
        if (points.length === 0) return;
        for (let i = points.length; i -= 1;) {
            const [x, y] = points[i];
            if (!drawn[`${x}:${y}`]) {
                drawn[`${x}:${y}`] = true;
                this.drawMarker(context, x, y);
            }
        }
    };

    drawMarker = (context, x, y) => {
        const x_ = Math.floor(x - this.MARKER_WIDTH / 2 + this.MAX_DIMENSION_OF_MARKER);
        const y_ = Math.floor(y - this.MARKER_HEIGHT + this.MAX_DIMENSION_OF_MARKER);
        context.drawImage(this.marker, x_, y_, this.MARKER_WIDTH, this.MARKER_HEIGHT);
    }; 

我已经做了一些优化:比如for循环,只画那些没有画出来的点,使用整数坐标等等。

在那之后,我得到了一些不错的结果,但是我的页面在谷歌浏览器上有点卡住了。尽管如此,令我惊讶的是,在 Firefox 中它运行得非常快,就像,非常非常快。所以我用谷歌浏览器的性能标签做了一些挖掘,我发现我的代码使用了大量的 CPU,而且速度很慢。

我还发现了这个article,它说 Chrome 使用一些启发式方法来确定它是使用 CPU 还是 GPU 来绘制画布。

所以,我的问题是,如何在 Chrome 上强制使用 GPU?有没有我可以设置的标志或类似的东西?您还有其他方法可以加快绘图过程吗?

【问题讨论】:

  • 在该示例中,Canvas 非常大,因此 Chrome 会自动选择使用 GPU。我的问题是,在我的示例中,Chrome 决定使用 CPU。我需要的是一种改变它并使用 GPU 的方法。
  • 您可以通过调整浏览器窗口大小或使用开发工具来更改视图大小。在较小的尺寸下,它的运行似乎没有任何变慢。
  • 添加canvas.style.transform = "matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1)"; 是否会进行更改。只是猜测。
  • 不,它没有:(

标签: javascript google-chrome firefox html5-canvas gpu


【解决方案1】:

问题在于 Chrome 显然将 SVG 图像保存在 CPU 中,并在每次调用 drawImage() 时将其栅格化。

只需自己进行光栅化,Chrome 的性能就会立即提升。

为此,请使用 createImageBitmap() 方法,该方法将创建一个 ImageBitmap,浏览器将能够直接将其存储在 GPU 的内存中。
Safari 刚刚在其最新版本的浏览器中公开了此方法,因此您可能仍想为其使用 polyfill。虽然在这种情况下,只需在画布上绘图就足够了,I made such a polyfill 确实包含一些大多数浏览器尚不支持的功能。

(async () => {
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  const select = document.querySelector("select");
  const html_img = new Image();
  const svg_str = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 25" width="18" height="25">
    <circle cx="9" cy="9" r="6"/>
  </svg>`;
  const svg_blob = new Blob([svg_str], {
    type: "image/svg+xml"
  });

  ctx.font = "20px sans-serif";
  ctx.fillStyle = "red";

  html_img.src = URL.createObjectURL(svg_blob);
  const sources = {
    html_img,
    bitmap: await createImageBitmap(svg_blob)
  };
  const times = [];
  await html_img.decode();

  anim();

  function anim() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (let y = 0; y < canvas.width; y += 10) {
      for (let x = 0; x < canvas.width; x += 5) {
        ctx.drawImage(sources[select.value], x, y);
      }
    }
    requestAnimationFrame(anim);

    // ultra rough FPS counter
    const now = performance.now();
    while (times.length > 0 && times[0] <= now - 1000) {
      times.shift();
    }
    times.push(now);
    fps = times.length;
    ctx.fillText(fps + "FPS", 30, 30);
  }
})();
<!-- createImageBitmap polyfill for old browsers --> <script src="https://cdn.jsdelivr.net/gh/Kaiido/createImageBitmap/dist/createImageBitmap.js"></script>
source: <select>
  <option value="bitmap">ImageBitmap</option>
  <option value="html_img">HTMLImage</option>
</select><br>
<canvas width="300" height="300"></canvas>

【讨论】:

    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 2014-12-21
    • 2012-06-21
    • 2013-06-08
    • 2014-12-27
    • 2015-03-09
    相关资源
    最近更新 更多