为什么 CSS3 滤镜效果比 HTML5 Canvas 等效滤镜效果更好?

Why are CSS3 filter effects more performant than HTML5 Canvas equivalents?

我目前正在尝试对 HTML5 Canvas 上的图像应用简单的滤镜效果。类似于此处定义的技术:

HTML5 Canvas image contrast 而在这个 HTML5 Rocks! blog post

但是,这种特殊方法需要遍历每个像素并在重绘之前应用修改器。对于我的特定用例,这需要 150 毫秒以上的时间来重绘我的图像(650 像素 x 650 像素 PNG)

使用 CSS3's filter property(对比度或亮度)应用相同的效果需要不到 10 毫秒。

我的问题是:CSS3 的过滤器 属性 如何工作 "under the hood"?为什么它的性能显着提高? 有没有办法在 Canvas 中实现类似的性能?

Canvas 通过 API 暴露。 JavaScript 是一个 运行 时间,即使这些天进行了严格的优化,如果您要通过 JavaScript 的逐像素访问来操作 2D 网格,您将支付所述访问的惩罚从 运行 时间一直到包含当前存储的图像的缓冲区,每个像素 canvas、。机器大多必然会遵循并实施您的数据访问模式,因此您可以说您最终成为这里性能的主要障碍,通过“过度控制”解决方案——通过将过滤器表示为命令式逐像素数据例如赋值,并且因为解释器在优化“自由形式”代码的能力方面通常受到限制。

CSS3 过滤器是一个黑盒,可以批量 执行相同的转换,这意味着 constrainedGPU shader program, for instance, is running directly on the GPU, accessing image that is typically stored in the immediately addressable memory close to the GPU, and the latter benefiting from its SIMD-class instructions 设计 来处理整个像素矩阵。此外,本机代码访问本地内存——几乎和它得到的一样快,没有深入细节。 GPU 使用很长的执行 pipeline —— 简而言之,是一长串操作,不会重新启动或检查,因此可以尽可能快地执行这些操作。后者的一个类比是一列重型火车经过一个它知道它不会停靠的车站,所以它可以以最快的速度通过它,就好像它不在那里一样。这是 GPU 用来快速处理数据的技巧之一——假设一个受限的环境,这样您就不必考虑“一切”并可以优化更多。

即使 运行 在 CPU 上运行时,在没有任何 GPU 帮助的情况下,我们谈论的是原生过滤器内核直接在 RAM 中操作图像,没有任何字节码,更重要的是没有考虑JavaScript 完全没有。您声明所需的过滤器,用户代理在 canvas 图像上调用过滤器程序,仅此而已。 CPU 也有 SIMD 指令来处理数据向量,这显然有很大帮助。过滤器代码甚至不是你的,你只是通过名称引用它。

现在,如果您可以将某种黑盒过滤器(例如 CSS 中可用的一种)直接应用于 canvas 像素数据 ,您可能会达到与 CSS 相同的速度——因为您所拥有的最重要的速度障碍——用 JavaScript 代码表示的逐像素访问——已被消除。所以这不仅仅是关于 JavaScript,这是关于数据访问的 粒度 。在这种情况下,将 kernel 应用于批量数据总是比自己在更高级别上编写内核代码更快。简单来说,这让我想到了下面的最后一点。

现在,如果 JavaScript 解释器可以 理解 你的逐像素循环操作,它可以将它全部转换成使用 SIMD 的本机代码,甚至可能 GPU shaders, all from your freely-typed JavaScript filter code, that would bridge the performance gap. But you would be moving the complexity into JavaScript compiler/interpreter, and the problem of optimizing on such scale isn't a completely solved problem yet in computer science. Maybe artificial intelligence and machine learning will help, I won't speculate on that. Mind you, I am not talking JIT-compiling 您的 JavaScript 等效本机代码,这多年来一直是常态,我说的是 识别 自由-输入 JavaScript 代码为类似于已知或什至任意图像内核的东西,就像人类一样。然后用 运行-time 对应的代码替换所述代码,该对应代码给出相同的结果但由编译器编写以产生它认为会提供最佳性能的代码。

在实践中,我认为你 可以 优化基于 Canvas JavaScript 朴素的逐像素过滤器,如果你深入研究Canvas API. The image data can be accessed through a Uint8ClampedArray type which you can obtain from CanvasRenderingContext2D.getImageData() method call. This array type has some interesting "bulk" functions like filter, forEach, map, reduce etc. You need to think a bit like a "hacker" when browsing the Canvas API documentation, to assume a mindset of a demoscene 人 -- 查看可用的方法和数据类型,看看它们能为您提供什么。这样做的回报是丰厚的。

如果这还不够,canvas 可以用 WebGL, an API which is a subset of OpenGL 渲染,通常只在 GPU 上实现 运行。着色器保证是 WebGL 的一部分,这意味着您可以获得使用 WebGL 的各种高级和超快 canvas 过滤器程序的免费门票,这些程序是您自己编写的,但通常在 GPU 上执行。