绘制大量线条时的HTML画布性能

时间:2021-03-13 16:51:54

I'm currently writing an application that displays a lot, and I mean, a lot of 2D paths (made of hundreds, thousands of tiny segments) on an HTML5 canvas. Typically, a few million points. These points are downloaded from a server into a binary ArrayBuffer.

我正在编写一个显示很多的应用程序,我的意思是,在HTML5画布上有很多2D路径(由数百个,数千个小段组成)。通常,几百万点。这些点从服务器下载到二进制ArrayBuffer中。

I probably won't be using that many points in the real world, but I'm kinda interested in how I could improve the performance. You can call it curiosity if you want ;)

我可能不会在现实世界中使用那么多点,但我对如何提高性能感兴趣。如果你愿意,可以称之为好奇心;)

Anyway, I've tested the following solutions :

无论如何,我测试了以下解决方案:

  1. Using gl.LINES or gl.LINE_STRIP with WebGL, and compute everything in shaders on the GPU. Currently the fastest, can display up to 10M segments without flinching on my Macbook Air. But there are very strict constraints for the binary format if you want to avoid processing things in JavaScript, which is slow.

    将gl.LINES或gl.LINE_STRIP与WebGL一起使用,并计算GPU上着色器中的所有内容。目前最快的,可以显示多达10M的细分,而不会在我的Macbook Air上退缩。但是如果你想避免在JavaScript中处理事情,那么对二进制格式有非常严格的限制。

  2. Using Canvas2D, draw a huge path with all the segments in one stroke() call. When I'm getting past 100k points, the page freezes for a few seconds before the canvas is updated. So, not working here.

    使用Canvas2D,在一个stroke()调用中绘制一条包含所有段的巨大路径。当我超过100k点时,页面会在画布更新前冻结几秒钟。所以,不在这里工作。

  3. Using Canvas2D, but draw each path with its own stroke() call. Despite what others have been saying on the internet, this is much faster than drawing everything in one call, but still a lot slower than WebGL. Things start to get bad when I reach about 500k segments.

    使用Canvas2D,但使用自己的stroke()调用绘制每个路径。尽管其他人一直在互联网上说,这比在一次调用中绘制所有内容要快得多,但仍然比WebGL慢很多。当我达到约500k段时,事情开始恶化。

The two Canvas2D solutions require looping through all the points of all the paths in JavaScript, so this is quite slow. Do you know of any method(s) that could improve JavaScript's iteration speed in an ArrayBuffer, or processing speed in general?

两个Canvas2D解决方案需要循环遍历JavaScript中所有路径的所有点,因此这非常慢。您是否知道可以提高JavaScript在ArrayBuffer中的迭代速度或一般处理速度的方法?

But, what's strange is, the screen isn't updated immediately after all the canvas draw calls have finished. When I start getting to the performance limit, there is a noticeable delay between the end of the draw calls and the update of the canvas. Do you have any idea where that comes from, and is there a way to reduce it?

但是,奇怪的是,在所有画布绘制调用完成后,屏幕不会立即更新。当我开始达到性能限制时,绘制调用结束和画布更新之间会有明显的延迟。你知道它来自哪里,有没有办法减少它?

1 个解决方案

#1


7  

First, WebGL was a nice and hype idea, but the amount of processing required to decode and display the binary data simply doesn't work in shaders, so I ruled it out.

首先,WebGL是一个很好的炒作理念,但解码和显示二进制数据所需的处理量在着色器中不起作用,因此我将其排除在外。

Here are the main bottlenecks I've encountered. Some of them are quite common in general programming, but it's a good idea to remember them :

以下是我遇到的主要瓶颈。其中一些在一般编程中很常见,但记住它们是个好主意:

  • It's best to use multiple, small for loops
  • 最好使用多个小的for循环

  • Create variables and closures at the highest level possible, don't create them inside the for loops
  • 尽可能在*别创建变量和闭包,不要在for循环中创建它们

  • Render your data in chunks, and use setTimeout to schedule the next chunk after a few milliseconds : that way, the user will still be able to use the UI
  • 以块的形式呈现数据,并使用setTimeout在几毫秒后调度下一个块:这样,用户仍然可以使用UI

  • JavaScript objects and arrays are fast and cheap, use them. It's best to read/write them in sequential order, from the beginning to the end.
  • JavaScript对象和数组快速而便宜,使用它们。最好从开始到结束按顺序读/写它们。

  • If you don't write data sequentially in an array, use objects (because non-sequential read-writes are cheap for objects) and push the indexes into an index array. I used a SortedList implementation to keep the indexes sorted, which I found here. Overhead was minimal (about 10-20% of the rendering time), and in the end it was well worth it.
  • 如果不在数组中按顺序写入数据,请使用对象(因为非顺序读写对于对象来说是便宜的)并将索引推送到索引数组中。我使用SortedList实现来保持索引的排序,我在这里找到了。开销很小(大约是渲染时间的10-20%),最后它非常值得。

That's about everything I remember. If I do find something else, I'll update this answer!

这就是我记得的一切。如果我找到别的东西,我会更新这个答案!

#1


7  

First, WebGL was a nice and hype idea, but the amount of processing required to decode and display the binary data simply doesn't work in shaders, so I ruled it out.

首先,WebGL是一个很好的炒作理念,但解码和显示二进制数据所需的处理量在着色器中不起作用,因此我将其排除在外。

Here are the main bottlenecks I've encountered. Some of them are quite common in general programming, but it's a good idea to remember them :

以下是我遇到的主要瓶颈。其中一些在一般编程中很常见,但记住它们是个好主意:

  • It's best to use multiple, small for loops
  • 最好使用多个小的for循环

  • Create variables and closures at the highest level possible, don't create them inside the for loops
  • 尽可能在*别创建变量和闭包,不要在for循环中创建它们

  • Render your data in chunks, and use setTimeout to schedule the next chunk after a few milliseconds : that way, the user will still be able to use the UI
  • 以块的形式呈现数据,并使用setTimeout在几毫秒后调度下一个块:这样,用户仍然可以使用UI

  • JavaScript objects and arrays are fast and cheap, use them. It's best to read/write them in sequential order, from the beginning to the end.
  • JavaScript对象和数组快速而便宜,使用它们。最好从开始到结束按顺序读/写它们。

  • If you don't write data sequentially in an array, use objects (because non-sequential read-writes are cheap for objects) and push the indexes into an index array. I used a SortedList implementation to keep the indexes sorted, which I found here. Overhead was minimal (about 10-20% of the rendering time), and in the end it was well worth it.
  • 如果不在数组中按顺序写入数据,请使用对象(因为非顺序读写对于对象来说是便宜的)并将索引推送到索引数组中。我使用SortedList实现来保持索引的排序,我在这里找到了。开销很小(大约是渲染时间的10-20%),最后它非常值得。

That's about everything I remember. If I do find something else, I'll update this answer!

这就是我记得的一切。如果我找到别的东西,我会更新这个答案!