【问题标题】:Canvas painting time issue with getImageData() in game loop游戏循环中 getImageData() 的画布绘制时间问题
【发布时间】:2022-01-09 13:42:00
【问题描述】:

我正在开发一个包含绘图循环的小型视频游戏,我在使用 MS Edge 时遇到了一种奇怪的行为(但它在 Chrome 上也以另一种方式发生)。

在某个特定时间,我想获取有关像素的数据并使用,只需一次ctx.getImageData() 它会在这个非常精确的时刻触发 CPU 内存增加(我知道这个函数很昂贵)......但是在循环的所有下一次迭代中,绘制时间急剧增加。

这里是链接:http://millgraphik.alwaysdata.net/cpu-test/

还有代码:http://millgraphik.alwaysdata.net/cpu-test/js/canvas.js

您可以使用 MS Edge 进行测试,在页面加载后立即启动性能记录。 getImageData 在页面加载后大约 3 秒触发。以下是性能测试结果:

绿色,绘画时长

那么绘画时间增加的原因是什么?

【问题讨论】:

  • 我已经看到了您的问题,但很难推理。现在你最好做一些实验。我会首先尝试在您调用 ctx.getImageData() 时将循环迭代延迟 200 毫秒,以查看油漆如何变化。
  • 我尝试了很多东西,包括延迟... :(

标签: javascript loops canvas memory cpu


【解决方案1】:

Chrome 和 Edge 现在都基于 Chromium,它使用 GPU 来加速 2D 画布操作。如果您导航到edge://flagschrome://flags(在Chrome 上)并禁用Accelerated 2D canvas,您会注意到缓慢的行为会立即开始,而不是在getImageData() 调用之后。这是因为画布现在默认由 CPU 渲染。

但是为什么在getImageData() 之后它开始很快然后变慢?使用 GPU 加速时,Chrome 在使用getImageData() 后似乎至少切换到基于 CPU 的渲染器。

这是来自相关Chromium bug ticket comment的引用:

...

Chrome 的问题是每次调用 getImageData 从 GPU 生成回读,这是一个缓慢的操作。我们 具有将画布切换到软件的性能启发式 大量使用 getImageData 时的渲染模式(无 GPU)。问题是 启发式查找在三个中使用 getImageData 的情况 连续的动画帧。我们需要改变这条规则 会遇到这样的情况。

因此,您的代码正在触发 Chromium 当前用于检测频繁的 getImageData() 调用的任何启发式方法。

Another related comment:

几年前,我们开始使用 gpu 来完成所有与画布相关的操作。 我们遇到的一个问题是 GPU 的运行速度明显慢于 CPU getImageData,但对于所有其他画布,GPU 比 CPU 快 操作。所以我们做了一个决定,第一个 getImageData 调用运行在 GPU 以及对 getImageData 的所有后续调用都在 CPU 上运行。这是 因为我们相信用户可能会做更多的 getImageData 调用,而我们 想要快速处理这些问题。

软件渲染(CPU)结果与 GPU 略有不同 渲染结果,也就是你在两者中观察到的差异 getImageData 调用。

您找到的标志,它允许您指定 getImageData 是否将 经常使用。如果是,请使用 CPU;否则,使用 GPU(意思是 调用 getImageData 不会导致渲染器的变化)。所以 显然,这将解决问题!

我们尚未准备好发布此功能 - 仍在测试中 阶段。我们计划在 M94-95 中推出它。这个问题就会过去 时间。

感谢您的反馈!

【讨论】:

  • 谢谢!这是一个非常高级的问题......在调用getImageData之后,我在chrome上体验了完全相同的切换cpu/gpu。所以现在的问题是:如何强制画布回到 GPU 引擎?我们可以吗 ?还是我离开这个功能?
  • 也许使用第二个画布来获取 getimagedata() 的结果...我会尝试看看它是否更糟。欢迎任何建议!
【解决方案2】:

我用 CSS display: none; 创建了一个离屏画布,并在这个离屏画布中使用getImageData() 获取我的像素数据。 它似乎用 GPU 来维持绘画。

这是新的测试:http://millgraphik.alwaysdata.net/cpu-test2/

和代码http://millgraphik.alwaysdata.net/cpu-test2/js/canvas.js

编辑:使用片段画布的更好解决方案:http://millgraphik.alwaysdata.net/cpu-test3/js/canvas.js

以及 Edge 上的性能测试(类似于 chrome):

您如何看待这种新方式?

【讨论】:

  • 很高兴知道。也许您也可以在文档片段下使用画布。
  • 片段解决方案工作正常。我想这是一种更有效的方法...这是新代码:millgraphik.alwaysdata.net/cpu-test3/js/canvas.js 欢迎使用此解决方案的任何 cmets
  • 太好了。现在我会尝试frag.append(ctx.cloneNode(true)) 并对frag 下的克隆ctx 执行all 操作。完成后,只需通过 rAF 执行ctx.replaceWith(frag)。现在 frag 再次变为空,并在下一个 rAF 循环中再次克隆 ctx 并继续进行。那个怎么样?喜欢here
  • 有什么好处?离屏 仅用于将画布从 G​​PU 切换到 CPU 的操作。不过好吧,我会试试的!
  • 我只是想把关注点分成两部分,比如处理和显示。让我们让所有操作离线,并使用实时 DOM 仅用于在 rAF 下显示。进一步的步骤可能是将您的上下文画布定义为自定义元素,并可能在 shadow DOM 下进行所有计算。无法判断,但 JS 引擎可能已经为这种情况开发了一些 GPU 或多核 CPU 处理。只是实验而已。
猜你喜欢
  • 2012-06-08
  • 1970-01-01
  • 1970-01-01
  • 2020-03-25
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多