javascript - 在绘制到屏幕之前在内存 Canvas 中进行修改会大大降低 Javascript 性能

标签 javascript performance canvas

我注意到如果内存中有大量 Canvas ,在将它们绘制到屏幕之前修改每个 Canvas 会大大降低我机器的性能。即使 Canvas 很小并且修改很小,也会发生这种情况。
这是我能想到的最人为的例子:

var { canvas, ctx } = generateCanvas();
ctx.strokeStyle = "#000";

var images = [];
for (var i = 0; i < 500; i++) {
  images.push(generateCanvas(50, "red"));
}

var fps = 0,
  lastFps = new Date().getTime();
requestAnimationFrame(draw);

function draw() {
  requestAnimationFrame(draw);

  var modRects = document.getElementById("mod-rects").checked;
  var drawRects = document.getElementById("draw-rects").checked;

  ctx.clearRect(0, 0, 500, 500);
  ctx.strokeRect(0, 0, 500, 500);

  fps++;
  if (new Date().getTime() - lastFps > 1000) {
    console.clear();
    console.log(fps);
    fps = 0;
    lastFps = new Date().getTime();
  }

  images.forEach(img => {
    img.ctx.fillStyle = "yellow";
    if (modRects) img.ctx.fillRect(20, 20, 10, 10);
    if (drawRects) ctx.drawImage(img.canvas, 225, 225);
  });
}

function generateCanvas(size = 500, color = "black") {
  var canvas = document.createElement("canvas");
  canvas.width = canvas.height = size;
  var ctx = canvas.getContext("2d");
  ctx.fillStyle = color;
  ctx.fillRect(0, 0, size, size);

  return {
    canvas,
    ctx
  };
}

function generateCheckbox(name) {
  var div = document.createElement("div");
  var check = document.createElement("input");
  check.type = "checkbox";
  check.id = name;
  var label = document.createElement("label");
  label.for = name;
  label.innerHTML = name;
  div.appendChild(check);
  div.appendChild(label);
  return div;
}

document.body.appendChild(canvas);
document.body.appendChild(generateCheckbox("mod-rects"));
document.body.appendChild(generateCheckbox("draw-rects"));
canvas+div+div { margin-bottom: 20px; }

在这个例子中,我们创建了 500 个大小为 50x50 的 Canvas 。在较大的屏幕 Canvas 下方有两个复选框。第一个导致在这 500 个 Canvas 中的每一个上绘制一个黄色的小方块。第二个导致 Canvas 被绘制到更大的 Canvas 上。 FPS 每秒发布到控制台一次。 I see no performance issues when one or the other checkbox is checked, but when both are checked, performance drops drastically.
我的第一个想法是它与在修改时每帧将内存 Canvas 发送到 gfx 卡有关。
这是我试图创造的实际效果。
image
视频:https://youtu.be/Vr6v2oF3G-8
代码:https://github.com/awhipple/base-command-dev/blob/e2c38946cdaf573abff5ded5399c90687ffa76a5/engine/gfx/shapes/Particle.js
我的最终目标是能够平滑过渡 Canvas 的颜色。我正在使用 globalCompositeOperation = "source-in"fillRect()在上面的代码链接中执行此操作。

最佳答案

如前所述,这是每一帧向 GPU 发送数百个 Canvas 的开销问题。当在 CPU 中修改 Canvas 时,它会被标记为“脏”,并在下次使用时重新发送到 GPU。
我找到的解决方法是创建一个包含粒子图像网格的大 Canvas 。每个粒子对象都会对其指定的网格部分进行修改。然后一旦完成所有修改,我们就开始进行绘制图像调用,根据需要切割较大的 Canvas
我还需要切换到 globalCompositeOperation = "source-atop"以防止每次我尝试更改粒子时所有其他粒子都被破坏。
代码:https://github.com/awhipple/base-command-dev/blob/2514327c6c30cb9914962d2c8d604f04bfbdbed5/engine/gfx/shapes/Particle.js
示例:http://avocado.whipple.life/
你可以在这里看到,当 this.newRender === true在 draw 中,它排队等待稍后绘制。
然后static drawQueuedParticles一旦每个粒子都有机会自己排队,就会调用它。
最终结果是这个更大的 Canvas 每帧只发送一次到 GPU。我在运行 2700 RTX GPU 且屏幕粒子数为 1500 的 Razorblade Pro 上看到性能从 15 FPS 提高到 60 FPS。

关于javascript - 在绘制到屏幕之前在内存 Canvas 中进行修改会大大降低 Javascript 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63327734/

相关文章:

javascript - 如何从动态创建的 div 的 ID 中获取值?

javascript - Angular 日期选择器不触发 ng-change

javascript - Canvas - 绘制线条但未显示在谷歌检查元素中

javascript - 如何使用javascript在html5的canvas中绘制图片

javascript - 将 Chart.js 连接到 MySQL 数据库

python - 引入多处理队列时执行时间增加

java - 读取数组中文件数据的最快方法(Java)

c++ - 双重简单的 for 循环与一个复杂的循环

php - 将 Canvas 和表单发送到服务器以自定义名称保存图像

javascript - 使用 .replaceWith 更改 dom